Python 确保特定列是数据帧中的最后一列(或第一列)的最快方法是什么
给定Python 确保特定列是数据帧中的最后一列(或第一列)的最快方法是什么,python,pandas,Python,Pandas,给定df df = pd.DataFrame(np.arange(8).reshape(2, 4), columns=list('abcd')) 假设我需要列'b'位于末尾。我可以做到: df[['a', 'c', 'd', 'b']] 但是,确保给定列位于末尾的最有效方法是什么 这就是我一直在做的。其他人会怎么做 def put_me_last(df, column): return pd.concat([df.drop(column, axis=1), df[column]]
df
df = pd.DataFrame(np.arange(8).reshape(2, 4), columns=list('abcd'))
假设我需要列'b'
位于末尾。我可以做到:
df[['a', 'c', 'd', 'b']]
但是,确保给定列位于末尾的最有效方法是什么
这就是我一直在做的。其他人会怎么做
def put_me_last(df, column):
return pd.concat([df.drop(column, axis=1), df[column]], axis=1)
put_me_last(df, 'b')
计时结果 结论 mfripp是赢家。似乎
reindex\u轴
比[]
具有更大的效率增益。这真是个好消息
代码
from string import lowercase
df_small = pd.DataFrame(np.arange(8).reshape(2, 4), columns=list('abcd'))
df_large = pd.DataFrame(np.arange(1000000).reshape(10000, 100),
columns=pd.MultiIndex.from_product([list(lowercase[:-1]), ['One', 'Two', 'Three', 'Four']]))
def pir1(df, column):
return pd.concat([df.drop(column, axis=1), df[column]], axis=1)
def pir2(df, column):
if df.columns[-1] == column:
return df
else:
pos = df.columns.values.__eq__('b').argmax()
return df[np.roll(df.columns, len(df.columns) - 1 - pos)]
def pir3(df, column):
if df.columns[-1] == column:
return df
else:
pos = df.columns.values.__eq__('b').argmax()
cols = df.columns.values
np.concatenate([cols[:pos], cols[1+pos:], cols[[pos]]])
return df[np.concatenate([cols[:pos], cols[1+pos:], cols[[pos]]])]
def pir4(df, column):
if df.columns[-1] == column:
return df
else:
return df[np.roll(df.columns.drop(column).insert(0, column), -1)]
def carsten1(df, column):
cols = list(df)
if cols[-1] == column:
return df
else:
return pd.concat([df.drop(column, axis=1), df[column]], axis=1)
def carsten2(df, column):
cols = list(df)
if cols[-1] == column:
return df
else:
idx = cols.index(column)
new_cols = cols[:idx] + cols[idx + 1:] + [column]
return df[new_cols]
def mfripp1(df, column):
new_cols = [c for c in df.columns if c != column] + [column]
return df[new_cols]
def mfripp2(df, column):
new_cols = [c for c in df.columns if c != column] + [column]
return df.reindex_axis(new_cols, axis='columns', copy=False)
def ptrj1(df, column):
return df.reindex(columns=df.columns.drop(column).append(pd.Index([column])))
def shivsn1(df, column):
column_list=list(df)
column_list.remove(column)
column_list.append(column)
return df[column_list]
def merlin1(df, column):
return df[df.columns.drop(["b"]).insert(99999, 'b')]
list_of_funcs = [pir1, pir2, pir3, pir4, carsten1, carsten2, mfripp1, mfripp2, ptrj1, shivsn1]
def test_pml(df, pml):
for c in df.columns:
pml(df, c)
summary = pd.DataFrame([], [f.__name__ for f in list_of_funcs], ['Small', 'Large'])
for f in list_of_funcs:
summary.at[f.__name__, 'Small'] = timeit(lambda: test_pml(df_small, f), number=100)
summary.at[f.__name__, 'Large'] = timeit(lambda: test_pml(df_large, f), number=10)
首先(而且,根据您的用例,也是最有效的)优化是首先确保您不必重新排列数据集。如果您想要成为最后一列的列已经就位,那么您可以不更改地返回df。试试这个:
def put_me_last2(df, column):
if list(df)[-1] == column:
return df
else: return pd.concat([df.drop(column, axis=1), df[column]], axis=1)
df.reindex(columns=df.columns.drop(col).append(pd.Index([col])))
我试过用800万个条目而不是你例子中的8个条目,当我要求列b
时,速度与上一列差不多,当我希望最后一列是d
时,速度快了300倍(500us vs 150ms)(即无需重新排序的情况)
如果你有很多列,或者通常想重新排列列,这对你没有帮助,但也没有坏处
更新:
我发现了一种更快的方法:不要删除并重新添加一列,而是使用df[cols]
和想要的列列表。给我大约40%的加速(90毫秒对150毫秒,有800万个条目)
我将重新排列列列表,而不是删除并追加其中一列:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.arange(8).reshape(2, 4), columns=list('abcd'))
def put_me_last(df, column):
return pd.concat([df.drop(column, axis=1), df[column]], axis=1)
def put_me_last_fast(df, column):
new_cols = [c for c in df.columns if c != column] + [column]
return df[new_cols]
def put_me_last_faster(df, column):
new_cols = [c for c in df.columns if c != column] + [column]
return df.reindex_axis(new_cols, axis='columns', copy=False)
计时(在iPython中):
注意:您可以使用下面的行定义新的_cols,但它比上面使用的慢80倍(2µs vs 160µs)
另请注意:如果您经常尝试将列移动到已存在的末尾,则可以通过添加此项将这些情况的时间缩短到1µs以下,如@Carsten所述:
if df.columns[-1] == column:
return df
这个怎么样:
def put_me_last2(df, column):
if list(df)[-1] == column:
return df
else: return pd.concat([df.drop(column, axis=1), df[column]], axis=1)
df.reindex(columns=df.columns.drop(col).append(pd.Index([col])))
(.append([col])
不起作用-可能是个bug。编辑:.append(pd.Index([col])
可能是append中最安全的选项。)
对测试的评论:如果您计划使用timeit
进行测试,请尝试在大型df(如1e4行或更多行)上运行它,并且可能使用-n1-r1
来防止缓存。从以下内容开始:
df.columns
Index([u'a', u'b', u'c', u'd'], dtype='object')
不要这样做,看起来像个虫子。
df.columns.drop(["b"]).insert(-1, 'b')
Index([u'a', u'c', u'b', u'd'], dtype='object')
df.columns.drop(["b"]).insert(-1, 'x')
Index([u'a', u'c', u'x', u'd'], dtype='object')
围绕以下方面工作:
df.columns.drop(["b"]).insert(99999, 'b')
Index([u'a', u'c', u'd', u'b'], dtype='object')
但这并不是最快的:
def put_me_last(df,column):
column_list=list(df)
column_list.remove(column)
column_list.append(column)
return df[column_list]
%timeit put_me_last(df,'b')
1000 loops, best of 3: 391 µs per loop
如果df.columns[-1],我会将其编辑为
==列:
。但是,是的,这是一个很好的提示。两种方法的工作方式都相同。其中一种可能会快几微秒。如果您进行测试,请将结果添加到我的答案或您的问题中,我会感兴趣的。我添加了一个额外的方法,可以提高40%的速度。请尝试将您的备选方案与模块进行比较。@mhawke我计划好了吗。这就是我将如何决定答案。到目前为止,您的两个备选方案中哪一个更快?我正在进行测试。我必须随机化列顺序,并在不同大小的数据集上测试许多试验。今晚我将有一个。@mhawke到目前为止的唯一答案只有在列已经是最后一个的情况下才有好处。在随机化设置中,它将获得这种好处1/len(columns)。根据列的数量,好处可能不会超过检查的成本。无论如何,我将构建一个适当的测试。IMO,这不是一个bug,它是Python的list.insert()
方法的标准行为。尝试以下方法:df.columns.drop('b')。insert(len(df.columns)-1,'b'))
@Merlin:这一点很好。它的行为是经过设计的,但如果你希望在索引末尾插入一个项,那就令人惊讶了。令人恼火的是,df.index.insert()的工作方式类似于list.insert(),但df.index.append()的工作方式不同于list.append()。这意味着没有完全自然的方式将一项添加到索引末尾(必须使用大数字或检索索引的长度)。
def put_me_last(df,column):
column_list=list(df)
column_list.remove(column)
column_list.append(column)
return df[column_list]
%timeit put_me_last(df,'b')
1000 loops, best of 3: 391 µs per loop