我每天都在工作中使用大熊猫。我最近从0.13.1升级到0.15.1,现在在迭代相对较小的DataFrame时,一堆代码太慢而无法完成。
(我意识到在DataFrame上通常有更好/更快的方法来完成迭代,但有时它非常清晰且简洁,有一个for循环结构)
我在混合类型时将问题缩小到一个问题:
def iterGet(df,col):
for i in df.index:
tmp = df[col].loc[i]
def iterLocSet(df,col,val):
for i in df.index:
#df[col].loc[i] = val
df.loc[i,col] = val
df.at[i,col] = val
return df
N = 100
df = pd.DataFrame(rand(N,3),columns = ['a','b','c'])
df['listCol'] = [[] for i in range(df.shape[0])]
df['strCol'] = [str(i) for i in range(df.shape[0])]
df['intCol'] = [i for i in range(df.shape[0])]
df['float64Col'] = [float64(i) for i in range(df.shape[0])]
print df.a[:5]
%time iterGet(df[['a','intCol']].copy(),'a')
%time tmpDf = iterLocSet(df[['a','intCol']].copy(),'a',0.)
print tmpDf.a[:5]
%time iterGet(df[['a','float64Col']].copy(),'a')
%time tmpDf = iterLocSet(df[['a','float64Col']].copy(),'a',0.)
print tmpDf.a[:5]
在Pandas 0.15.1上,结果是:
0 0.114738
1 0.586447
2 0.296024
3 0.446697
4 0.720984
Name: a, dtype: float64
Wall time: 6 ms
Wall time: 3.41 s
0 0
1 0
2 0
3 0
4 0
Name: a, dtype: float64
Wall time: 6 ms
Wall time: 18 ms
0 0
1 0
2 0
3 0
4 0
Name: a, dtype: float64
但是在Pandas 0.13.1上,结果如下:
0 0.651796
1 0.738661
2 0.885366
3 0.513006
4 0.846323
Name: a, dtype: float64
Wall time: 6 ms
Wall time: 14 ms
0 0
1 0
2 0
3 0
4 0
Name: a, dtype: float64
Wall time: 5 ms
Wall time: 15 ms
0 0
1 0
2 0
3 0
4 0
Name: a, dtype: float6
在Pandas 0.15.1中,使用多类型数组上的行索引进行分配似乎要慢200倍?
我知道通过分配可能是数组的副本可能存在潜在的陷阱,但我承认我也不完全理解这个问题。至少我可以看到作业正在发挥作用。 EDIT 虽然我现在看到在for循环中使用其中任何一个都可以解决问题:
df.loc[i,col] = val
df.at[i,col] = val
我不太了解诊断这一点的实施方法。任何人都可以重现这个吗?这是你所期望的吗?我究竟做错了什么? 谢谢!
答案 0 :(得分:2)
即使在单一的dtyped框架上使用.loc
,也可以在部分分配中生成数据的副本。 (当你有对象 dtypes时,这几乎总是如此,对于数字类型则更少)。
部分分配时,我的意思是:
df.loc[1,'B'] = value
IOW。这是在这种情况下设置单个值(设置多个值类似)。但是,设置列是非常不同的。
df['B'] = values
df[:,'B'] = values
效率很高,不会复制。
因此,您应该完全避免迭代,只需这样做。
df['B'] = [ ..... ] # if you want to set with a list-like
df['B'] = value # for a scalar
因此,在上面的示例中,它可能会在每次迭代时进行复制。 0.13.1在处理部分赋值时有点麻烦,并且会错误地处理某些情况,因此需要复制一些。