有效地从 Pandas DataFrame 中提取行而忽略缺少的索引标签

Dan*_*ler 6 indexing dataframe pandas

我正在寻找更有效的等价物

df.reindex(labels).dropna(subset=[0])
Run Code Online (Sandbox Code Playgroud)

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

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

df.loc[labels]
Run Code Online (Sandbox Code Playgroud)

默默地忽略不在 中的标签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)
Run Code Online (Sandbox Code Playgroud)

最后 2 个示例比迭代 的示例快约 1000 倍df.index。这表明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)
Run Code Online (Sandbox Code Playgroud)

这仍然比建议的解决方案快约 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)
Run Code Online (Sandbox Code Playgroud)

所以要明确的是,我追求的东西比df.reindex(labels).dropna(). 这已经是次线性的,df.shape[0]并且不对指数做任何假设,因此解决方案也应该如此。

我要解决的问题是,df.reindex(labels)将包含NaN缺少标签的行,然后需要使用dropna. 我在等价于df.reindex(labels)不把它们放在首位,没有扫描整个df.index找出丢失的标签。这至少在原则上必须是可能的:如果reindex可以通过插入虚拟行有效地即时处理丢失的标签,那么应该可以通过什么都不做而更有效地即时处理它们。

Max*_*axU 11

这是不同方法的一个小比较。

\n\n

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

\n\n
np.random.seed([3, 1415])\ndf = pd.DataFrame(np.random.rand(10**7, 2))\nlabels = np.random.randint(10**9, size=10**4)\n\nIn [88]: df.shape\nOut[88]: (10000000, 2)\n
Run Code Online (Sandbox Code Playgroud)\n\n

有效(现有标签):

\n\n
In [89]: (labels <= 10**7).sum()\nOut[89]: 1008\n
Run Code Online (Sandbox Code Playgroud)\n\n

无效(不存在标签):

\n\n
In [90]: (labels > 10**7).sum()\nOut[90]: 98992\n
Run Code Online (Sandbox Code Playgroud)\n\n

时间:

\n\n
In [103]: %timeit df[df.index.isin(set(labels))]\n943 ms \xc2\xb1 7.86 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n\nIn [104]: %timeit df.loc[df.index.intersection(set(labels))]\n360 ms \xc2\xb1 1.65 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n\nIn [105]: %timeit df.loc[np.intersect1d(df.index, labels)]\n513 ms \xc2\xb1 655 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n
Run Code Online (Sandbox Code Playgroud)\n