删除行/获取与Pandas中的大型DataFrame不同的子集的最快方法



问题

我正在寻找最快的方法来从一个大的Pandas DataFrame中删除一组我已经拥有的索引行,或者获得这些索引的差异的子集(这会导致相同的数据集(。

到目前为止,我有两个解决方案,对我来说似乎相对较慢:

  1. df.loc[df.difference(indices)]

    在我的数据集上大约需要115秒

  2. df.drop(indices)

    在我的数据集上大约需要215秒

有更快的方法吗?最好是熊猫。

拟定解决方案的性能

  • ~41秒:@jezrael的df[~df.index.isin(indices)]

我相信你可以创建布尔掩码,通过~反转,通过boolean indexing:过滤

df1 = df[~df.index.isin(indices)]

如@user3471881所述,如果您计划稍后操作已过滤的df,则有必要添加copy:以避免链式索引

df1 = df[~df.index.isin(indices)].copy()

此过滤取决于匹配索引的数量以及DataFrame的长度。

因此,另一种可能的解决方案是创建索引的array/list以保持,然后不需要反转:

df1 = df[df.index.isin(need_indices)]

使用iloc(或loc,见下文(和Series.drop:

df = pd.DataFrame(np.arange(0, 1000000, 1))
indices = np.arange(0, 1000000, 3)
%timeit -n 100 df[~df.index.isin(indices)]
%timeit -n 100 df.iloc[df.index.drop(indices)]
41.3 ms ± 997 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
32.7 ms ± 1.06 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

正如@jezrael所指出的,如果indexRangeIndex,则只能使用iloc,否则必须使用loc。但这仍然比df[df.isin()]快(见下文原因(。

1000万行上的所有三个选项:

df = pd.DataFrame(np.arange(0, 10000000, 1))
indices = np.arange(0, 10000000, 3)
%timeit -n 10 df[~df.index.isin(indices)]
%timeit -n 10 df.iloc[df.index.drop(indices)]
%timeit -n 10 df.loc[df.index.drop(indices)]
4.98 s ± 76.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
752 ms ± 51.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
2.65 s ± 69.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

为什么超慢loc优于boolean_indexing

简单的答案是没有。df.index.drop(indices)只比~df.index.isin(indices)快很多(上面给出的数据有1000万行(:

%timeit -n 10 ~df.index.isin(indices)
%timeit -n 10 df.index.drop(indices)
4.55 s ± 129 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
388 ms ± 10.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

我们可以将其与boolean_indexingilocloc的性能进行比较:

boolean_mask = ~df.index.isin(indices)
dropped_index = df.index.drop(indices)
%timeit -n 10 df[boolean_mask]
%timeit -n 10 df.iloc[dropped_index]
%timeit -n 10 df.loc[dropped_index]

489 ms ± 25.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
371 ms ± 10.6 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
2.38 s ± 153 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

如果行的顺序不介意,可以将它们排列到位:

n=10**7
df=pd.DataFrame(arange(4*n).reshape(n,4))
indices=np.unique(randint(0,n,size=n//2))
from numba import njit
@njit
def _dropfew(values,indices):
k=len(values)-1
for ind in indices[::-1]:
values[ind]=values[k]
k-=1
def dropfew(df,indices):
_dropfew(df.values,indices)
return df.iloc[:len(df)-len(indices)]

运行次数:

In [39]: %time df.iloc[df.index.drop(indices)]
Wall time: 1.07 s
In [40]: %time dropfew(df,indices)
Wall time: 219 ms

最新更新