有效地从pandas DataFrame中提取行,忽略丢失的索引标签

时间:2018-04-14 09:39:55

标签: pandas dataframe indexing

我正在寻找更高效的

df.reindex(labels).dropna(subset=[0])

避免在结果中包含缺少标签的NaN行,而不必在reindex将它们放入后删除它们。

同样地,我正在寻找一个有效版本的

df.loc[labels]

无声地忽略不在df.index中的标签,即结果的行数可能少于labels的元素。

当行,列和标签的数量都很大并且存在明显的未命中率时,我需要一些有效的东西。具体来说,我正在寻找数据集长度的次线性。

更新1

以下是@ MaxU回答后问题的具体演示:

In [2]: L = 10**7
   ...: M = 10**4
   ...: N = 10**9
   ...: np.random.seed([3, 1415])
   ...: df = pd.DataFrame(np.random.rand(L, 2))
   ...: labels = np.random.randint(N, size=M)
   ...: M-len(set(labels)) 
   ...: 
   ...: 
Out[2]: 0

In [3]: %timeit df[df.index.isin(set(labels))]
904 ms ± 59.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [4]: %timeit df.loc[df.index.intersection(set(labels))]
207 ms ± 11.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [5]: %timeit df.loc[np.intersect1d(df.index, labels)]
427 ms ± 37 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [6]: %timeit df.loc[labels[labels<L]]
329 µs ± 23 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [7]: %timeit df.iloc[labels[labels<L]]
161 µs ± 8.35 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

最后两个示例比在df.index上迭代的示例快〜1000倍。这表明df.loc[labels]不会迭代索引,并且数据帧具有有效的索引结构,即df.index确实索引。

所以问题是当df.loc[labels[labels<L]]不是连续的数字序列时,如何获得与df.index一样高效的东西。部分解决方案是原始的

In [8]: %timeit df.reindex(labels).dropna(subset=[0])
1.81 ms ± 187 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

这仍然比建议的解决方案快〜100倍,但仍然可能会失去一个数量级。

更新2

进一步证明,即使没有对索引进行上瘾,也可以获得次线性表现,并使用字符串索引重复上述内容

In [16]: df.index=df.index.map(str)
    ...: labels = np.array(list(map(str, labels)))
    ...: 
    ...: 

In [17]: %timeit df[df.index.isin(set(labels))]
657 ms ± 48.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [18]: %timeit df.loc[df.index.intersection(set(labels))]
974 ms ± 160 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [19]: %timeit df.reindex(labels).dropna()
8.7 ms ± 121 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

所以要清楚我要追求比df.reindex(labels).dropna()更有效率的东西。这在df.shape[0]中已经是次线性的,并且没有对索引做出任何假设,因此解决方案也应如此。

我想解决的问题是,df.reindex(labels)将包含NaN行,用于丢失标签,然后需要使用dropna删除。我追求的是相当于df.reindex(labels)的{​​@ 1}},并没有将它们放在那里,而没有扫描整个df.index以找出丢失的标签。这至少在原则上是必要的:如果reindex可以通过插入虚拟行来有效地处理丢失的标签,那么应该可以通过无所事事来更有效地处理它们。

1 个答案:

答案 0 :(得分:3)

以下是不同方法的小比较。

样品DF(形状:10.000.000 x 2):

np.random.seed([3, 1415])
df = pd.DataFrame(np.random.rand(10**7, 2))
labels = np.random.randint(10**9, size=10**4)

In [88]: df.shape
Out[88]: (10000000, 2)

有效(现有标签):

In [89]: (labels <= 10**7).sum()
Out[89]: 1008

无效(不是现有标签):

In [90]: (labels > 10**7).sum()
Out[90]: 98992

<强>时序:

In [103]: %timeit df[df.index.isin(set(labels))]
943 ms ± 7.86 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [104]: %timeit df.loc[df.index.intersection(set(labels))]
360 ms ± 1.65 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [105]: %timeit df.loc[np.intersect1d(df.index, labels)]
513 ms ± 655 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)