我有以下DataFrame
:
dates = pd.date_range('20150101', periods=4)
df = pd.DataFrame({'A' : [5,10,3,4]}, index = dates)
df.loc[:,'B'] = 0
df.loc[:,'C'] = 0
df.iloc[0,1] = 10
df.iloc[0,2] = 3
print df
Out[69]:
A B C
2015-01-01 5 10 3
2015-01-02 10 0 0
2015-01-03 3 0 0
2015-01-04 4 0 0
我想为列B
和C
:
B(k+1) = B(k) - A(k+1)
C(k+1) = B(k) + A(k+1)
我可以使用以下代码执行此操作:
for i in range (1, df.shape[0]):
df.iloc[i,1] = df.iloc[i-1,1] - df.iloc[i,0]
df.iloc[i,2] = df.iloc[i-1,1] + df.iloc[i,0]
print df
这给出了:
A B C
2015-01-01 5 10 3
2015-01-02 10 0 20
2015-01-03 3 -3 3
2015-01-04 4 -7 1
我正在寻找的答案。问题是当我将其应用于具有大型数据集的DataFrame
时,它运行缓慢。非常慢。有没有更好的方法来实现这一目标?
答案 0 :(得分:6)
向量化的技巧是将所有内容重写为cumsums。
In [11]: x = df["A"].shift(-1).cumsum().shift().fillna(0)
In [12]: x
Out[12]:
2015-01-01 0
2015-01-02 10
2015-01-03 13
2015-01-04 17
Name: A, dtype: float64
In [13]: df["B"].cumsum() - x
Out[13]:
2015-01-01 10
2015-01-02 0
2015-01-03 -3
2015-01-04 -7
dtype: float64
In [14]: df["B"].cumsum() - x + 2 * df["A"]
Out[14]:
2015-01-01 20
2015-01-02 20
2015-01-03 3
2015-01-04 1
dtype: float64
注意:第一个值是特殊情况,因此您必须将其调整为3。
答案 1 :(得分:2)
像这样的递归事件可能难以矢量化。 numba
通常会很好地处理它们 - 如果您需要重新分发代码,cython
可能是更好的选择,因为它会产生没有额外依赖关系的常规c扩展。
In [88]: import numba
In [89]: @numba.jit(nopython=True)
...: def logic(a, b, c):
...: N = len(a)
...: out = np.zeros((N, 2), dtype=np.int64)
...: for i in range(N):
...: if i == 0:
...: out[i, 0] = b[i]
...: out[i, 1] = c[i]
...: else:
...: out[i, 0] = out[i-1,0] - a[i]
...: out[i, 1] = out[i-1,0] + a[i]
...: return out
In [90]: logic(df.A.values, df.B.values, df.C.values)
Out[90]:
array([[10, 3],
[ 0, 20],
[-3, 3],
[-7, 1]], dtype=int64)
In [91]: df[['A','B']] = logic(df.A.values, df.B.values, df.C.values)
编辑: 如其他答案所示,这个问题实际上可以进行矢量化,你应该使用它。
答案 2 :(得分:1)
基本上只有你的答案没有for循环:
df['B'].iloc[1:] = df['B'].iloc[:-1].values - df['A'].iloc[1:].values
df['C'].iloc[1:] = df['B'].iloc[:-1].values + df['A'].iloc[1:].values
我不知道性能问题,但我想如果没有循环它会更快。
答案 3 :(得分:1)
完整的解决方案:
df1 = df[:1]
df['B'] = df['B'].shift().cumsum()[1:] - df['A'][1:].cumsum()
df[:1] = df1
df['C'] = df['B'].shift() + df['A']
df[:1] = df1
df
A B C
2015-01-01 5 10 3
2015-01-02 10 0 20
2015-01-03 3 -3 3
2015-01-04 4 -7 1
答案 4 :(得分:0)
您可以提取numpy中的基础数组,并获得更快,更简单的解决方案。在这里,我们可以获得130倍的加速比。
dates = pd.date_range('20150101', periods=10000)
df = pd.DataFrame({'A' : np.random.rand(10000)}, index = dates)
df.loc[:,'B'] = 0
df.loc[:,'C'] = 0
df.iloc[0,1] = 10
df.iloc[0,2] = 3
旧的,慢速的熊猫版本:
%%time
for i in range (1, df.shape[0]):
df.iloc[i,1] = df.iloc[i-1,1] - df.iloc[i,0]
df.iloc[i,2] = df.iloc[i-1,1] + df.iloc[i,0]
CPU times: user 6.02 s, sys: 3.33 ms, total: 6.02 s
Wall time: 5.98 s
更快,更好的版本:
%%time
arr_a = df['A'].to_numpy()
arr_b = df['B'].to_numpy()
arr_c = df['C'].to_numpy()
for i in range(1, df.shape[0]):
arr_b[i] = arr_b[i - 1] - arr_a[i]
arr_c[i] = arr_b[i - 1] + arr_a[i]
CPU times: user 47.6 ms, sys: 23 µs, total: 47.6 ms
Wall time: 46 ms
numpy数组引用了原始内存,因此更改numpy数组也会修改数据帧。
熊猫索引缓慢。他们希望您使用矢量化运算,但是对于这样的问题,很难获得3位博士学位的要求才能了解熊猫需要哪些组,合并和索引的集合。拔出底层数组并直接进行修改会更简单,更快捷。