【问题标题】:Pandas: Need a speedier way of index slicingPandas:需要一种更快的索引切片方式
【发布时间】:2016-07-21 21:16:37
【问题描述】:

有人愿意尝试加快这个数据帧索引切片方案的速度吗? 我正在尝试对一些巨大的数据帧进行切片和切块,因此每一位都很重要。我需要以某种方式找到一种更快的索引切片数据帧的方法,而不是以下技术:

v = initFrame.xs(x,level=('ifoo2','ifoo3'), drop_level=False) 

pd.unique 中的循环也会显着影响性能。

uniqueList = list(pd.unique(initFrame[['bar1','bar4']].values))

复制并粘贴下面的 sn-p 以避免设置。

import pandas as pd

foo1 = (['LABEL1','LABEL1','LABEL2','LABEL2'])
foo2 = ([5,5,6,6])
foo3 = ([1,1,2,3])

index = pd.MultiIndex.from_arrays([foo1,foo2,foo3], names=['ifoo1','ifoo2','ifoo3'])

initFrame = pd.DataFrame({'bar1': [ 5,6,5,6],
                          'bar2': ['a','b','c','d'],
                          'bar3': [11,22,33,44],
                          'bar4': [1,2,1,3]}, index=index)
                           
finDict = {}
#start timer1
uniqueList = list(pd.unique(initFrame[['bar1','bar4']].values))
#end timer1
for x in uniqueList:
    #start timer2
    v = initFrame.xs(x,level=('ifoo2','ifoo3'), drop_level=False)
    #stop timer2
    k = int(x[0]), int(x[1])  
    finDict.update({k:v})

更新 2016-04-04

对于那些感兴趣的人,我最终使用了以下内容:

finDict = {}
grouper = initFrame.groupby(level=('ifoo2', 'ifoo3'))
for name, group in grouper:
    finDict.update({name:group})

【问题讨论】:

  • 你关心两个定时器之间的速度,还是uniqueList中的循环?
  • 你说得对!没有完成我的帖子。已编辑,谢谢。
  • 执行 uniquelist,然后再一次 for 循环就像针对 1 个可能的循环执行 3 x 循环。再次考虑到您的数据集很大,准备工作将永远进行。

标签: python pandas dataframe slice


【解决方案1】:

您可以将字典理解与loc 一起使用来进行数据帧索引:

finDict = {pair: df.loc[pd.IndexSlice[:, pair[0], pair[1]], :] 
           for pair in pd.unique(initFrame[['bar1', 'bar4']].values).tolist()}

>>> finDict
{(5, 1):                     bar1 bar2  bar3  bar4
 ifoo1  ifoo2 ifoo3                       
 LABEL1 5     1         5    a    11     1
              1         6    b    22     2,
 (6, 2):                     bar1 bar2  bar3  bar4
 ifoo1  ifoo2 ifoo3                       
 LABEL2 6     2         5    c    33     1,
 (6, 3):                     bar1 bar2  bar3  bar4
 ifoo1  ifoo2 ifoo3                       
 LABEL2 6     3         6    d    44     3}

【讨论】:

  • 很好理解
【解决方案2】:

我不知道你真正想做什么,但这里有一些提示可以加快你的代码:

改变

uniqueList = list(pd.unique(initFrame[['bar1','bar4']].values))

uniqueList = initFrame[["bar1", "bar4"]].drop_duplicates().values.tolist()

和for循环:

g = initFrame.groupby(level=(1, 2))
uniqueSet = set(uniqueList)
dict((key, df) for key, df in g if key in uniqueSet)

或:

g = initFrame.groupby(level=(1, 2))
dict((key, g.get_group(key)) for key in uniqueList)

这是 %timeit 比较:

import numpy as np
import pandas as pd
arr = np.random.randint(0, 10, (10000, 2))
df = pd.DataFrame(arr, columns=("A", "B"))

%timeit df.drop_duplicates().values.tolist()
%timeit list(pd.unique(arr))

输出:

100 loops, best of 3: 3.51 ms per loop
10 loops, best of 3: 94.7 ms per loop

【讨论】:

  • drop_duplicates 不会比pd.unique 慢吗?我想我们可以用iterrows() 循环一次——因为多索引是分组的,如果 ('bar1', 'bar4') 的值等于前一个,我们可以简单地比较并跳过循环,然后执行 dict更新。
  • @Anzel,我添加了一个 %timeit 测试,你可以看看结果。速度提高了 20 倍。
  • 我的:%timeit initFrame[["bar1", "bar4"]].drop_duplicates().values.tolist() 1000 loops, best of 3: 1.12 ms per loop%timeit list(pd.unique(initFrame[['bar1','bar4']].values)) 1000 loops, best of 3: 470 µs per loop。但我想如果数据集足够大,你的可能会很高效
  • 我确认 drop_duplicates 确实快 20 倍,因为数组很大。我有和你类似的输出。为我的第一个无知评论道歉:-)
  • @HYRY 仅供参考。 pd.unique 用于纯 dtypes,使用元组有效地使用 object 慢得多。尝试使用单列并查看差异。 .drop_duplicates() 基本上是分解事物,然后调用 .unique()
【解决方案3】:

不是作为答案,而只是为了可视化我的评论,因为多索引是分组的,我们可以简单地&可能只是比较并跳过循环如果 ('bar1', 'bar4') 的值 等于之前的值,然后执行字典更新。

它可能不会更快,但如果你的数据集很大,它可能会为你节省内存消耗问题,伪代码:

# ...replace timer1...
prev, finDict = None, {}
for n in initFrame[['bar1', 'bar4']].iterrows():
    current = (n[0][1], n[0][2])
    if current == prev: continue
    prev = current
    #... whatever faster way to solve your 2nd timer...

我个人认为@Alexander 很好地回答了你的第二个计时器。

【讨论】:

    猜你喜欢
    • 2020-07-16
    • 2015-03-16
    • 2018-12-29
    • 2018-03-13
    • 2021-08-25
    • 2015-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多