从 Pandas DataFrame 中删除一列
Posted
技术标签:
【中文标题】从 Pandas DataFrame 中删除一列【英文标题】:Delete a column from a Pandas DataFrame 【发布时间】:2018-05-01 00:22:44 【问题描述】:在我使用的 DataFrame 中删除列时:
del df['column_name']
这很好用。为什么我不能使用以下内容?
del df.column_name
由于可以以df.column_name
的身份访问列/系列,我希望这可以工作。
【问题讨论】:
注意这个问题正在Meta讨论。 【参考方案1】:在 Pandas 中执行此操作的最佳方法是使用 drop
:
df = df.drop('column_name', 1)
其中1
是轴 编号(0
表示行,1
表示列。)
要删除列而不必重新分配df
,您可以这样做:
df.drop('column_name', axis=1, inplace=True)
最后,要按列 number 而不是按列 label 删除,试试这个删除,例如第 1、2、4 列:
df = df.drop(df.columns[[0, 1, 3]], axis=1) # df.columns is zero-based pd.Index
还使用列的“文本”语法:
df.drop(['column_nameA', 'column_nameB'], axis=1, inplace=True)
注意:在 v0.21.0(2017 年 10 月 27 日)中引入,drop() 方法接受 index/columns 关键字作为指定轴的替代方法。
所以我们现在可以这样做:
df = df.drop(columns=['column_nameA', 'column_nameB'])
【讨论】:
出于某种原因,是否推荐使用del
?
虽然这种删除方法有其优点,但这个答案并不能真正回答所提出的问题。
True @Paul,但由于问题的标题,大多数到达这里的人都会尝试找出如何删除列。
@beardc drop
相对于del
的另一个优势是drop
允许您一次删除多个列,执行就地或不执行操作,还可以删除沿任意轴的记录(尤其是适用于 3-D 矩阵或Panel
)
drop
相对于del
的另一个优势是drop 是pandas API 的一部分并包含文档。【参考方案2】:
如您所料,正确的语法是
del df['column_name']
仅仅由于 Python 中的语法限制,很难让del df.column_name
工作。 del df[name]
被 Python 翻译成 df.__delitem__(name)
。
【讨论】:
我意识到这是一个非常古老的“答案”,但我的好奇心被激起了 - 为什么这是 Python 的语法限制?class A(object): def __init__(self): self.var = 1
设置一个类,然后a = A(); del a.var
工作正常...
@dwanderson 不同之处在于,当要删除一列时,DataFrame 需要有自己的“如何做”处理。在del df[name]
的情况下,它被转换为df.__delitem__(name)
,这是DataFrame 可以根据需要实现和修改的一种方法。在del df.name
的情况下,成员变量被删除,没有机会运行任何自定义代码。考虑您自己的示例-您可以让del a.var
打印“删除变量”吗?如果可以的话,请告诉我怎么做。我不能:)
@Yonatan 您可以使用docs.python.org/3/reference/datamodel.html#object.__delattr__ 或描述符:docs.python.org/3/howto/descriptor.html
@Yonatan Eugene 的评论也适用于 Python 2;描述符自 2.2 以来一直在 Python 2 中,满足您的要求很简单;)
这个答案并不正确 - pandas
开发人员没有,但这并不意味着它很难做到。【参考方案3】:
用途:
columns = ['Col1', 'Col2', ...]
df.drop(columns, inplace=True, axis=1)
这将就地删除一列或多列。请注意,inplace=True
是在 pandas v0.13 中添加的,不适用于旧版本。在这种情况下,您必须将结果分配回去:
df = df.drop(columns, axis=1)
【讨论】:
【参考方案4】:按索引拖放
删除第一、二、四列:
df.drop(df.columns[[0,1,3]], axis=1, inplace=True)
删除第一列:
df.drop(df.columns[[0]], axis=1, inplace=True)
有一个可选参数inplace
这样原来的
无需创建副本即可修改数据。
弹出
Column selection, addition, deletion
删除列column-name
:
df.pop('column-name')
示例:
df = DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6]), ('C', [7,8, 9])], orient='index', columns=['one', 'two', 'three'])
print df
:
one two three
A 1 2 3
B 4 5 6
C 7 8 9
df.drop(df.columns[[0]], axis=1, inplace=True)
print df
:
two three
A 2 3
B 5 6
C 8 9
three = df.pop('three')
print df
:
two
A 2
B 5
C 8
【讨论】:
【参考方案5】:这里的大多数答案都错过了提出的实际问题是:
为什么我不能使用del df.column_name
?
首先我们需要了解问题所在,这需要我们深入了解Python magic methods。
正如Wes points out 在他的回答中,del df['column']
映射到 Python 魔术方法 df.__delitem__('column')
即implemented in Pandas to drop the column。
但是,正如上面关于Python magic methods 的链接中指出的那样:
事实上,
__del__
几乎不应该被使用,因为它被调用的环境不稳定;谨慎使用!
您可以争辩说不应使用或鼓励使用 del df['column_name']
,因此甚至不应考虑使用 del df.column_name
。
但是,理论上,del df.column_name
可以使用the magic method __delattr__
在 Pandas 中工作。然而,这确实引入了某些问题,del df['column_name']
实现已经存在的问题,但程度较轻。
示例问题
如果我在名为“dtypes”或“columns”的数据框中定义一列会怎样?
然后假设我想删除这些列。
del df.dtypes
会使__delattr__
方法混淆,好像它应该删除“dtypes”属性或“dtypes”列。
这个问题背后的架构问题
-
数据框是列的集合吗?
数据框是行的集合吗?
列是数据框的属性吗?
熊猫回答:
-
是的,在所有方面
不,但如果您愿意,可以使用
.ix
、.loc
或.iloc
方法。
也许,您要读取数据吗?然后是,除非属性的名称已经被属于数据框的另一个属性占用。您要修改数据吗?然后没有。
TLDR;
你不能这样做del df.column_name
,因为 Pandas 有一个相当疯狂的架构,需要重新考虑它的用户才不会发生这种认知失调。
专业提示:
不要使用 df.column_name。它可能很漂亮,但它会导致认知失调。
适合此处的 Python 引用之禅:
删除列的方法有多种。
应该有一种——最好只有一种——明显的方法。
列有时是属性,但有时不是。
特殊情况不足以打破规则。
del df.dtypes
是否删除了 dtypes 属性或 dtypes 列?
面对模棱两可,拒绝猜测的诱惑。
【讨论】:
实际上解决了原始问题的 WHY 部分。我已经实现了 pandas 数据框的子类。这样做会教你这个答案的重要部分。区分属性和列名是一个大问题。 df.a 无论 a 是属性还是列名,都会留下歧义。但是,正如 pandas 所写,df["a"] 只能是一个列。【参考方案6】:一个不错的附加功能是仅当列存在时才删除它们。这样你可以覆盖更多的用例,它只会从传递给它的标签中删除现有的列:
只需添加 errors='ignore',例如:
df.drop(['col_name_1', 'col_name_2', ..., 'col_name_N'], inplace=True, axis=1, errors='ignore')
这是从 pandas 0.16.1 开始的新功能。文档是here。
【讨论】:
【参考方案7】:从 0.16.1 版本开始,您可以这样做
df.drop(['column_name'], axis = 1, inplace = True, errors = 'ignore')
【讨论】:
如果需要这样的应用程序,这还支持删除多个列,其中一些不需要存在(即不会引发错误errors= 'ignore'
)df.drop(['column_1','column_2'], axis=1 , inplace=True,errors= 'ignore')
!【参考方案8】:
最好始终使用[]
表示法。原因之一是属性表示法 (df.column_name
) 不适用于编号索引:
In [1]: df = DataFrame([[1, 2, 3], [4, 5, 6]])
In [2]: df[1]
Out[2]:
0 2
1 5
Name: 1
In [3]: df.1
File "<ipython-input-3-e4803c0d1066>", line 1
df.1
^
SyntaxError: invalid syntax
【讨论】:
【参考方案9】:熊猫 0.21+ 答案
Pandas 0.21 版略微更改了 drop
方法以包含 index
和 columns
参数以匹配 rename
和 reindex
方法的签名。
df.drop(columns=['column_a', 'column_c'])
就个人而言,我更喜欢使用axis
参数来表示列或索引,因为它是几乎所有pandas 方法中使用的主要关键字参数。但是,现在您在 0.21 版中有一些额外的选择。
【讨论】:
【参考方案10】:在 Pandas 0.16.1+ 中,只有当它们存在于 the solution posted by eiTan LaVi 时,您才能删除列。在该版本之前,您可以通过条件列表推导获得相同的结果:
df.drop([col for col in ['col_name_1','col_name_2',...,'col_name_N'] if col in df],
axis=1, inplace=True)
【讨论】:
【参考方案11】:用途:
df.drop('columnname', axis =1, inplace = True)
否则你可以去
del df['colname']
根据列号删除多列
df.drop(df.iloc[:,1:3], axis = 1, inplace = True)
根据列名删除多个列
df.drop(['col1','col2',..'coln'], axis = 1, inplace = True)
【讨论】:
【参考方案12】:TL;DR
为了找到一个稍微更有效的解决方案付出了很多努力。在牺牲df.drop(dlst, 1, errors='ignore')
的简单性的同时,很难证明增加的复杂性是合理的
df.reindex_axis(np.setdiff1d(df.columns.values, dlst), 1)
序言 删除一列在语义上与选择其他列相同。我将展示一些额外的方法来考虑。
我还将重点介绍一次删除多个列并允许尝试删除不存在的列的一般解决方案。
使用这些解决方案是通用的,也适用于简单的情况。
设置
考虑pd.DataFrame
df
并列出要删除的dlst
df = pd.DataFrame(dict(zip('ABCDEFGHIJ', range(1, 11))), range(3))
dlst = list('HIJKLM')
df
A B C D E F G H I J
0 1 2 3 4 5 6 7 8 9 10
1 1 2 3 4 5 6 7 8 9 10
2 1 2 3 4 5 6 7 8 9 10
dlst
['H', 'I', 'J', 'K', 'L', 'M']
结果应该是这样的:
df.drop(dlst, 1, errors='ignore')
A B C D E F G
0 1 2 3 4 5 6 7
1 1 2 3 4 5 6 7
2 1 2 3 4 5 6 7
由于我将删除一列等同于选择其他列,因此我将其分为两种类型:
-
标签选择
布尔选择
标签选择
我们首先制作标签列表/数组,这些标签代表我们想要保留的列,而没有我们想要删除的列。
df.columns.difference(dlst)
Index(['A', 'B', 'C', 'D', 'E', 'F', 'G'], dtype='object')
np.setdiff1d(df.columns.values, dlst)
array(['A', 'B', 'C', 'D', 'E', 'F', 'G'], dtype=object)
df.columns.drop(dlst, errors='ignore')
Index(['A', 'B', 'C', 'D', 'E', 'F', 'G'], dtype='object')
list(set(df.columns.values.tolist()).difference(dlst))
# does not preserve order
['E', 'D', 'B', 'F', 'G', 'A', 'C']
[x for x in df.columns.values.tolist() if x not in dlst]
['A', 'B', 'C', 'D', 'E', 'F', 'G']
标签中的列 为了比较选择过程,假设:
cols = [x for x in df.columns.values.tolist() if x not in dlst]
然后我们可以评估
df.loc[:, cols]
df[cols]
df.reindex(columns=cols)
df.reindex_axis(cols, 1)
所有评估结果为:
A B C D E F G
0 1 2 3 4 5 6 7
1 1 2 3 4 5 6 7
2 1 2 3 4 5 6 7
布尔切片
我们可以构造一个用于切片的布尔数组/列表
~df.columns.isin(dlst)
~np.in1d(df.columns.values, dlst)
[x not in dlst for x in df.columns.values.tolist()]
(df.columns.values[:, None] != dlst).all(1)
布尔值列 为了比较
bools = [x not in dlst for x in df.columns.values.tolist()]
df.loc[: bools]
所有评估结果为:
A B C D E F G
0 1 2 3 4 5 6 7
1 1 2 3 4 5 6 7
2 1 2 3 4 5 6 7
稳健的时序
函数
setdiff1d = lambda df, dlst: np.setdiff1d(df.columns.values, dlst)
difference = lambda df, dlst: df.columns.difference(dlst)
columndrop = lambda df, dlst: df.columns.drop(dlst, errors='ignore')
setdifflst = lambda df, dlst: list(set(df.columns.values.tolist()).difference(dlst))
comprehension = lambda df, dlst: [x for x in df.columns.values.tolist() if x not in dlst]
loc = lambda df, cols: df.loc[:, cols]
slc = lambda df, cols: df[cols]
ridx = lambda df, cols: df.reindex(columns=cols)
ridxa = lambda df, cols: df.reindex_axis(cols, 1)
isin = lambda df, dlst: ~df.columns.isin(dlst)
in1d = lambda df, dlst: ~np.in1d(df.columns.values, dlst)
comp = lambda df, dlst: [x not in dlst for x in df.columns.values.tolist()]
brod = lambda df, dlst: (df.columns.values[:, None] != dlst).all(1)
测试
res1 = pd.DataFrame(
index=pd.MultiIndex.from_product([
'loc slc ridx ridxa'.split(),
'setdiff1d difference columndrop setdifflst comprehension'.split(),
], names=['Select', 'Label']),
columns=[10, 30, 100, 300, 1000],
dtype=float
)
res2 = pd.DataFrame(
index=pd.MultiIndex.from_product([
'loc'.split(),
'isin in1d comp brod'.split(),
], names=['Select', 'Label']),
columns=[10, 30, 100, 300, 1000],
dtype=float
)
res = res1.append(res2).sort_index()
dres = pd.Series(index=res.columns, name='drop')
for j in res.columns:
dlst = list(range(j))
cols = list(range(j // 2, j + j // 2))
d = pd.DataFrame(1, range(10), cols)
dres.at[j] = timeit('d.drop(dlst, 1, errors="ignore")', 'from __main__ import d, dlst', number=100)
for s, l in res.index:
stmt = '(d, (d, dlst))'.format(s, l)
setp = 'from __main__ import d, dlst, , '.format(s, l)
res.at[(s, l), j] = timeit(stmt, setp, number=100)
rs = res / dres
rs
10 30 100 300 1000
Select Label
loc brod 0.747373 0.861979 0.891144 1.284235 3.872157
columndrop 1.193983 1.292843 1.396841 1.484429 1.335733
comp 0.802036 0.732326 1.149397 3.473283 25.565922
comprehension 1.463503 1.568395 1.866441 4.421639 26.552276
difference 1.413010 1.460863 1.587594 1.568571 1.569735
in1d 0.818502 0.844374 0.994093 1.042360 1.076255
isin 1.008874 0.879706 1.021712 1.001119 0.964327
setdiff1d 1.352828 1.274061 1.483380 1.459986 1.466575
setdifflst 1.233332 1.444521 1.714199 1.797241 1.876425
ridx columndrop 0.903013 0.832814 0.949234 0.976366 0.982888
comprehension 0.777445 0.827151 1.108028 3.473164 25.528879
difference 1.086859 1.081396 1.293132 1.173044 1.237613
setdiff1d 0.946009 0.873169 0.900185 0.908194 1.036124
setdifflst 0.732964 0.823218 0.819748 0.990315 1.050910
ridxa columndrop 0.835254 0.774701 0.907105 0.908006 0.932754
comprehension 0.697749 0.762556 1.215225 3.510226 25.041832
difference 1.055099 1.010208 1.122005 1.119575 1.383065
setdiff1d 0.760716 0.725386 0.849949 0.879425 0.946460
setdifflst 0.710008 0.668108 0.778060 0.871766 0.939537
slc columndrop 1.268191 1.521264 2.646687 1.919423 1.981091
comprehension 0.856893 0.870365 1.290730 3.564219 26.208937
difference 1.470095 1.747211 2.886581 2.254690 2.050536
setdiff1d 1.098427 1.133476 1.466029 2.045965 3.123452
setdifflst 0.833700 0.846652 1.013061 1.110352 1.287831
fig, axes = plt.subplots(2, 2, figsize=(8, 6), sharey=True)
for i, (n, g) in enumerate([(n, g.xs(n)) for n, g in rs.groupby('Select')]):
ax = axes[i // 2, i % 2]
g.plot.bar(ax=ax, title=n)
ax.legend_.remove()
fig.tight_layout()
这与运行df.drop(dlst, 1, errors='ignore')
所需的时间有关。经过这么多努力,我们似乎只能适度提高性能。
事实上,最好的解决方案使用reindex
或reindex_axis
破解list(set(df.columns.values.tolist()).difference(dlst))
。紧随其后但仍然比drop
稍微好一点的是np.setdiff1d
。
rs.idxmin().pipe(
lambda x: pd.DataFrame(
dict(idx=x.values, val=rs.lookup(x.values, x.index)),
x.index
)
)
idx val
10 (ridx, setdifflst) 0.653431
30 (ridxa, setdifflst) 0.746143
100 (ridxa, setdifflst) 0.816207
300 (ridx, setdifflst) 0.780157
1000 (ridxa, setdifflst) 0.861622
【讨论】:
【参考方案13】:我们可以通过drop()方法remove或delete指定列或指定列。
假设 df 是一个数据框。
要删除的列 = column0
代码:
df = df.drop(column0, axis=1)
要删除多列 col1、col2、. . . , coln, 我们必须在列表中插入所有需要删除的列。然后通过 drop() 方法删除它们。
代码:
df = df.drop([col1, col2, . . . , coln], axis=1)
【讨论】:
【参考方案14】:如果你的原始数据框 df
不是太大,你没有内存限制,你只需要保留几列,或者,如果你事先不知道名称所有您不需要的额外列,那么您不妨创建一个仅包含您需要的列的新数据框:
new_df = df[['spam', 'sausage']]
【讨论】:
【参考方案15】:使用dataframe
和slicing
的iloc
函数删除列,当我们有一个带有不需要值的典型列名时:
df = df.iloc[:,1:] # Removing an unnamed index column
这里0
是默认行,1
是第一列,因此:,1:
是我们删除第一列的参数。
【讨论】:
【参考方案16】:点语法在 javascript 中有效,但在 Python 中无效。
蟒蛇:del df['column_name']
JavaScript:del df['column_name']
或 del df.column_name
【讨论】:
【参考方案17】:在 Pandas DataFrame 中删除列的另一种方法
如果您不是在寻找 in-place 删除,那么您可以通过使用 DataFrame(...)
函数指定列来创建一个新的 DataFrame:
my_dict = 'name' : ['a','b','c','d'], 'age' : [10,20,25,22], 'designation' : ['CEO', 'VP', 'MD', 'CEO']
df = pd.DataFrame(my_dict)
创建一个新的 DataFrame 为
newdf = pd.DataFrame(df, columns=['name', 'age'])
您获得的结果与使用 del / drop 获得的结果一样好。
【讨论】:
这在技术上是正确的,但必须列出要保留的每一列而不是只列出要删除的一(或几)列似乎很愚蠢。【参考方案18】:要删除特定列之前和之后的列,您可以使用方法truncate。例如:
A B C D E
0 1 10 100 1000 10000
1 2 20 200 2000 20000
df.truncate(before='B', after='D', axis=1)
输出:
B C D
0 10 100 1000
1 20 200 2000
【讨论】:
【参考方案19】:从一般 Python 的角度来看,如果属性 column_name
可以删除,del obj.column_name
是有意义的。它需要是一个常规属性 - 或具有已定义删除器的属性。
这不能转化为 Pandas 并且 对 Pandas Dataframes 有意义的原因是:
认为df.column_name
是一个“虚拟属性”,它本身并不是一个东西,它不是那个专栏的“座位”,它只是一种访问专栏的方式。很像没有删除器的属性。
【讨论】:
以上是关于从 Pandas DataFrame 中删除一列的主要内容,如果未能解决你的问题,请参考以下文章
Pandas学习笔记,如何删除DataFrame中的一列(行)
Pandas Dataframe - 如何检查列中数值的符号,如果为负则删除符号并在发生这种情况时创建另一列?
如何删除某一列中值为 NaN 的 Pandas DataFrame 行