选择/排除熊猫中的列集



我想根据列选择从现有数据帧创建视图或数据帧。

例如,我想从数据帧df1创建一个数据帧df2,该数据帧包含除其中两个列之外的所有列。我尝试执行以下操作,但没有用:

import numpy as np
import pandas as pd
# Create a dataframe with columns A,B,C and D
df = pd.DataFrame(np.random.randn(100, 4), columns=list('ABCD'))
# Try to create a second dataframe df2 from df with all columns except 'B' and D
my_cols = set(df.columns)
my_cols.remove('B').remove('D')
# This returns an error ("unhashable type: set")
df2 = df[my_cols]

我做错了什么?也许更一般地说,熊猫有什么机制来支持从数据帧中挑选和排除任意列集?

您可以删除不需要的列或选择您需要的列

# Using DataFrame.drop
df.drop(df.columns[[1, 2]], axis=1, inplace=True)
# drop by Name
df1 = df1.drop(['B', 'C'], axis=1)
# Select the ones you want
df1 = df[['a','d']]

有一个名为 difference 的新索引方法。它返回原始列,并删除作为参数传递的列。

在这里,结果用于从df中删除列BD

df2 = df[df.columns.difference(['B', 'D'])]

请注意,这是一种基于集合的方法,因此重复的列名会导致问题,并且列顺序可能会更改。


优于drop的优势:当您只需要列列表时,您不会创建整个数据帧的副本。例如,为了删除列子集上的重复项:

# may create a copy of the dataframe
subset = df.drop(['B', 'D'], axis=1).columns
# does not create a copy the dataframe
subset = df.columns.difference(['B', 'D'])
df = df.drop_duplicates(subset=subset)

另一个选项,无需在循环中删除或过滤:

import numpy as np
import pandas as pd
# Create a dataframe with columns A,B,C and D
df = pd.DataFrame(np.random.randn(100, 4), columns=list('ABCD'))
# include the columns you want
df[df.columns[df.columns.isin(['A', 'B'])]]
# or more simply include columns:
df[['A', 'B']]
# exclude columns you don't want
df[df.columns[~df.columns.isin(['C','D'])]]
# or even simpler since 0.24
# with the caveat that it reorders columns alphabetically 
df[df.columns.difference(['C', 'D'])]

你真的不需要把它转换成一个集合:

cols = [col for col in df.columns if col not in ['B', 'D']]
df2 = df[cols]

还可以查看内置的DataFrame.filter函数。

简约但贪婪的方法(对于给定的df足够(:

df.filter(regex="[^BD]")

保守/懒惰方法(仅限完全匹配(:

df.filter(regex="^(?!(B|D)$).*$")

保守和通用:

exclude_cols = ['B','C']
df.filter(regex="^(?!({0})$).*$".format('|'.join(exclude_cols)))

你有 4 列 A,B,C,D

这是选择新数据帧所需列的更好方法:-

df2 = df1[['A','D']]

如果您希望改用列号,请使用:-

df2 = df1[[0,3]]

您只需要将set转换为list

import pandas as pd
df = pd.DataFrame(np.random.randn(100, 4), columns=list('ABCD'))
my_cols = set(df.columns)
my_cols.remove('B')
my_cols.remove('D')
my_cols = list(my_cols)
df2 = df[my_cols]
下面

介绍如何创建不包括列列表的DataFrame的副本

df = pd.DataFrame(np.random.randn(100, 4), columns=list('ABCD'))
df2 = df.drop(['B', 'D'], axis=1)

但要小心!你在问题中提到了观点,暗示如果你改变了df,你也希望df2改变。(就像数据库中的视图一样。

此方法无法实现:

>>> df.loc[0, 'A'] = 999 # Change the first value in df
>>> df.head(1)
     A         B         C         D
0  999 -0.742688 -1.980673 -0.920133
>>> df2.head(1) # df2 is unchanged. It's not a view, it's a copy!
          A         C
0  0.251262 -1.980673

另请注意,@piggybox的方法也是如此。(尽管这种方法很好,很流畅,而且是Pythonic。我不会做下去!!

有关视图与副本的更多信息,请参阅此 SO 答案以及该答案所指的 Pandas 文档的这一部分。

同样,在读取文件时,可能希望预先排除列,而不是浪费地将不需要的数据读取到内存中,然后丢弃它们。

从 pandas 0.20.0 开始,usecols 现在接受可调用对象。1 此更新允许更灵活的列读取选项:

skipcols = [...]
read_csv(..., usecols=lambda x: x not in skipcols)

后一种模式本质上与传统usecols方法相反 - 仅跳过指定的列。


鉴于

文件中的数据

import numpy as np
import pandas as pd

df = pd.DataFrame(np.random.randn(100, 4), columns=list('ABCD'))
filename = "foo.csv"
df.to_csv(filename)

法典

skipcols = ["B", "D"]
df1 = pd.read_csv(filename, usecols=lambda x: x not in skipcols, index_col=0)
df1

输出

          A         C
0  0.062350  0.076924
1 -0.016872  1.091446
2  0.213050  1.646109
3 -1.196928  1.153497
4 -0.628839 -0.856529
...

数据帧已写入文件。 然后它作为单独的数据帧读回,现在跳过不需要的列(BD(。

请注意,对于 OP 的情况,由于数据已经创建,更好的方法是接受答案,它会从现有对象中删除不需要的列。 但是,此处介绍的技术在将数据从文件直接读取到数据帧时最有用。

在本期中提出了对"skipcols"选项的请求,并在以后的期刊中得到了解决。

最新更新