为什么pd.concat({},axis = 1)比pd.concat({},axis = 0)慢.unstack(0)?

piR*_*red 7 python dataframe pandas

考虑一下这个熊猫系列词典.所有系列的索引都是整数,并且有一些潜在的重叠,但肯定不重合.当我有大量索引,大量非重叠以及许多要连接的项目pd.concat时,我做了一个看起来似乎很慢的东西axis=1.它促使我离开axis=0并随后离开unstack().我最终得到了同样的结果.但卸垛速度更快.

有没有人知道为什么会这样?

我认为这个连接系列应该很快,但我猜想卸载过程几乎完全相同pd.concat(axis=1).

dict_of_series = {
    's%s' % i: pd.Series(
        1, np.unique(np.random.randint(1000, 10000, size=1000))
    ) for i in range(100)
}
Run Code Online (Sandbox Code Playgroud)
%%timeit
pd.concat(dict_of_series, axis=0).unstack(0)
Run Code Online (Sandbox Code Playgroud)

10个循环,最佳3:29.6 ms每个循环

%%timeit
pd.concat(dict_of_series, axis=1)
Run Code Online (Sandbox Code Playgroud)

10个循环,最佳3:每循环43.1 ms

ptr*_*trj 3

快速分析显示,调用中的大量时间pd.concat(dict_of_series, axis=1)都消耗在pd.Index.union. 它在内部函数内对每个索引一一调用_union_indexes- 与下面我的函数类似union。

另一方面,pd.concat(dict_of_series, axis=0).unstack(0)利用了一些巧妙的优化,我没有尝试分析。无论如何,没有电话_union_indexes,甚至pd.Index.union没有。

“_union_indexes”会是罪魁祸首吗?很可能。我只取了索引:

idxs = [d.index for d in dict_of_series.values()]
Run Code Online (Sandbox Code Playgroud)

并比较了以下两个函数:

def union(idxs):
    ans = pd.Index([])
    for idx in idxs:
        ans = ans.union(idx)
    return ans

def union_multi(idxs):
    arr0 = np.concatenate(idxs)
    arr1 = np.zeros(arr0.size)
    ans = pd.MultiIndex.from_arrays([arr0, arr1])
    return ans.levels[0]
Run Code Online (Sandbox Code Playgroud)

union相当于_union_indexes, whileunion_multi具有一些多索引开销,但避免调用pd.Index.union.

我的粗略时间安排:

>>> %timeit -n1 -r1 pd.concat(dict_of_series, axis=1)
1 loop, best of 1: 82.9 ms per loop
>>> %timeit -n1 -r1 pd.concat(dict_of_series, axis=0).unstack(0)
1 loop, best of 1: 57.9 ms per loop

>>> %timeit -n1 -r1 union(idxs)
1 loop, best of 1: 32.8 ms per loop
>>> %timeit -n1 -r1 union_multi(idxs)
1 loop, best of 1: 12.5 ms per loop
Run Code Online (Sandbox Code Playgroud)

简单的速度pd.Index(np.unique(np.concatenate(idxs)))稍快一些,但它不适用于混合类型。

(顺便说一句,pd.concat仅当所有索引都是唯一的时才有效。)