从 Pandas DataFrame 中删除一列
- 2024-12-03 08:44:00
- admin 原创
- 159
问题描述:
要删除 DataFrame 中的一列,我可以成功使用:
del df['column_name']
但为什么我不能使用以下内容?
del df.column_name
由于可以通过访问该系列df.column_name
,因此我希望它能够起作用。
解决方案 1:
在 Pandas 中执行此操作的最佳方式是使用drop
:
df = df.drop('column_name', axis=1)
其中1
是轴编号(0
对于行和1
对于列。)
或者,该drop()
方法接受index
/columns
关键字作为指定轴的替代方法。所以我们现在就可以这样做:
df = df.drop(columns=['column_nameA', 'column_nameB'])
这是在 v0.21.0(2017 年 10 月 27 日)中引入的
要删除列而不重新分配,df
您可以执行以下操作:
df.drop('column_name', axis=1, inplace=True)
最后,要按列号而不是按列标签删除,请尝试以下操作,例如删除第 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)
解决方案 2:
正如你所猜测的,正确的语法是
del df['column_name']
del df.column_name
由于 Python 的语法限制,它很难简单地工作。由 Python 在幕后del df[name]
进行翻译。df.__delitem__(name)
解决方案 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-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 的魔法方法。
正如韦斯在他的回答中指出的那样,del df['column']
映射到在 Pandas 中实现的 Python魔术方法 以删除该列。df.__delitem__('column')
但是,正如上面的链接中关于Python 魔法方法所指出的:
事实上,
__del__
由于它被称为不稳定的情况,几乎不应该使用它;请谨慎使用它!
您可能会争辩说,del df['column_name']
不应使用或鼓励这种做法,因此del df.column_name
甚至不应考虑这种做法。
然而,理论上,可以使用魔法方法在del df.column_name
Pandas 中实现。但这确实引入了某些问题,这些问题在实现中已经存在,但程度较轻。__delattr__
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 列?
面对模糊性,拒绝猜测的诱惑。
解决方案 6:
一个不错的补充是只有当列存在时才可以删除它们。这样你可以覆盖更多的用例,并且它只会从传递给它的标签中删除现有的列:
只需添加errors='ignore',例如:
df.drop(['col_name_1', 'col_name_2', ..., 'col_name_N'], inplace=True, axis=1, errors='ignore')
这是 pandas 0.16.1 及以上版本的新功能。文档在此处。
解决方案 7:
从 0.16.1 版本开始,你可以这样做
df.drop(['column_name'], 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:
Pandas 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+ 中,您只能删除存在且由 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:
总结
花费大量精力寻找稍微更有效的解决方案。很难证明增加复杂性的同时牺牲了简单性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 ()方法删除指定的一个或多个列。
假设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:
如果您想找到从数据框中删除的简单方法,请按照以下步骤操作:column_name
`df`
df = df[df.columns.drop('column_name')]
解决方案 16:
当我们有一个包含不需要的值的典型列名时,使用和iloc
函数删除一列:dataframe
`slicing`
df = df.iloc[:,1:] # Removing an unnamed index column
这0
是默认行,1
是第一列,因此:,1:
是我们删除第一列的参数。
解决方案 17:
点语法在 JavaScript 中有效,但在 Python 中无效。
Python:
del df['column_name']
JavaScript:
del df['column_name']
或del df.column_name
解决方案 18:
删除 Pandas DataFrame 中列的另一种方法
如果您不想就地删除,那么您可以通过使用函数指定列来创建一个新的 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 获得的结果一样好。
解决方案 19:
通过使用自动完成或“IntelliSense”来利用字符串文字:
del df[df.column1.name]
# or
df.drop(df.column1.name, axis=1, inplace=True)
它与当前的 Pandas 版本配合良好。
解决方案 20:
使用 删除列del
不仅有问题(正如@firelynx所解释的那样),而且速度非常慢。例如,它比 慢约 37 倍drop()
。
from timeit import timeit
setup = "import pandas as pd; df=pd.DataFrame([range(10000)])"
for _ in range(3):
t1 = timeit("df = df.drop(columns=df.columns[0])", setup, number=10000)
t2 = timeit("del df[df.columns[0]]", setup, number=10000)
print(f"{t2/t1:.2f}")
# 37.40
# 37.45
# 37.34
就性能而言,如果需要删除单个列,布尔索引(创建所需列的布尔系列并loc
对其进行索引)实际上是完成这项工作的最快方法。但是,如果需要删除多个列,drop()
则是最快的方法。
作为复习,所讨论的方法如下(本页给出的所有方法都经过测试,但这两种方法速度最快)。
import pandas as pd
df = pd.DataFrame([range(10)]*5).add_prefix('col')
# drop a single column (the performance comparison is shown in LHS subplot)
df1 = df.loc[:, df.columns != 'col2'] # boolean indexing
df2 = df.drop(columns='col2') # drop
# drop multiple columns (the performance comparison is shown in RHS subplot)
df1 = df.loc[:, ~df.columns.isin(['col2', 'col4'])] # boolean indexing
df2 = df.drop(columns=['col2', 'col4']) # drop
以下性能比较图是使用 perfplot 库(timeit
在后台执行测试)创建的。这支持了上述说法。主要结论是,当删除单列时,布尔索引速度更快;但是,当删除非常宽的数据框的多列时,drop()
速度更快。
用于生成性能图的代码:
import pandas as pd
import perfplot
import random
import matplotlib.pyplot as plt
plt.figure(figsize=(12,5), facecolor='white')
plt.subplot(1, 2, 1)
perfplot.plot(
setup=lambda n: pd.DataFrame([range(n+1)]),
kernels=[lambda df: df.drop(columns=df.columns[0]), lambda df: df.loc[:, df.columns != df.columns[0]]],
labels= ['drop', 'boolean indexing'],
n_range=[2**k for k in range(21)],
xlabel='Number of columns in a dataframe',
title='Removing a single column from a dataframe',
equality_check=pd.DataFrame.equals)
plt.subplot(1, 2, 2)
perfplot.plot(
setup=lambda n: (pd.DataFrame([range(n+1)]), random.sample(range(n+1), k=(n+1)//2)),
kernels=[lambda df,cols: df.drop(columns=cols), lambda df,cols: df.loc[:, ~df.columns.isin(cols)]],
labels= ['drop', 'boolean indexing'],
n_range=[2**k for k in range(21)],
xlabel='Number of columns in a dataframe',
title='Removing multiple columns from a dataframe',
equality_check=pd.DataFrame.equals)
plt.tight_layout();
解决方案 21:
要删除特定列之前和之后的列,可以使用方法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
解决方案 22:
从一般的 Python 角度来看, del obj.column_name
如果属性可以删除,那么就有意义了column_name
。它需要是一个常规属性 - 或具有已定义删除器的属性。
这无法转化为 Pandas 并且对于 Pandas Dataframes没有意义的原因是:
将其视为
df.column_name
“虚拟属性”,它本身不是一个事物,它不是该列的“位置”,它只是访问该列的一种方式。很像没有删除器的属性。