首页 > 解决方案 > Pandas - 当 N 存储在一列中时,新列基于另一列 N 行的值

问题描述

我有一个带有示例数据的熊猫数据框:

idx    price    lookback
0      5    
1      7        1
2      4        2
3      3        1
4      7        3
5      6        1

回溯可以是正数负数,但我想取它的绝对值来获取多少行来取值。

我正在尝试创建一个包含pricefrom lookback + 1rows ago 的值的新列,例如:

idx    price    lookback  lb_price
0      5        NaN       NaN
1      7        1         NaN
2      4        2         NaN
3      3        1         7
4      7        3         5
5      6        1         3

我从感觉最明显的方式开始,这不起作用:

df['sbc'] = df['price'].shift(dataframe['lb'].abs() + 1)

然后我尝试使用 lambda,这没有用,但我可能做错了:

sbc = lambda c, x: pd.Series(zip(*[c.shift(x+1)]))
df['sbc'] = sbc(df['price'], df['lb'].abs())

我还尝试了一个循环(非常慢,但有效),但我确信有更好的方法:

lookback = np.nan
for i in range(len(df)):
    if df.loc[i, 'lookback']:
        if not np.isnan(df.loc[i, 'lookback']):
            lookback = abs(int(df.loc[i, 'lookback']))
    if not np.isnan(lookback) and (lookback + 1) < i:
        df.loc[i, 'lb_price'] = df.loc[i - (lookback + 1), 'price']

我已经看到了使用lambda,的示例df.apply,也许,Series.map但它们对我来说并不清楚,因为我是 Python 和 Pandas 的新手。

如果有不使用循环的方法,我正在寻找最快的方法。

另外,为了它的价值,我计划使用这个计算列来创建另一个列,我可以这样做:

df['streak-roc'] = 100 * (df['price'] - df['lb_price']) / df['lb_price']

但是,如果我可以将所有这些组合成一种真正有效的方法,那将是理想的。

解决方案!

提供的几个解决方案效果很好(谢谢!)但都需要一些小的调整来处理我的负数潜力,这是一个回顾 + 1 而不是 - 1,所以我觉得在这里发布我的修改是谨慎的。

所有这些都比我的原始循环要快得多,原始循环需要5m 26s来处理我的数据集。

我将我观察到的最快的一个标记为公认的,因为我提高循环的速度是主要目标。

编辑解决方案

从 Manas Sambare - 41 秒

df['lb_price'] = df.apply(
    lambda x: df['price'][x.name - (abs(int(x['lookback'])) + 1)]
    if not np.isnan(x['lookback']) and x.name >= (abs(int(x['lookback'])) + 1)
    else np.nan,
    axis=1)

从 mannh - 43 秒

def get_lb_price(row, df):
    if not np.isnan(row['lookback']):
        lb_idx = row.name - (abs(int(row['lookback'])) + 1)
        if lb_idx >= 0:
            return df.loc[lb_idx, 'price']
        else: 
            return np.nan

df['lb_price'] = dataframe.apply(get_lb_price, axis=1 ,args=(df,))

从比尔 - 18 秒

lookup_idxs = df.index.values - (abs(df['lookback'].values) + 1)
valid_lookups = lookup_idxs >= 0
df['lb_price'] = np.nan
df.loc[valid_lookups, 'lb_price'] = df['price'].to_numpy()[lookup_idxs[valid_lookups].astype(int)]

标签: pythonpandasdataframe

解决方案


通过使用 row.name 在 df.apply() 调用中获取行的索引,您可以生成与当前所在行相关的“lb_price”数据。

%time
df.apply(
    lambda x: df['price'][x.name - int(x['lookback'] + 1)]
    if not np.isnan(x['lookback']) and x.name >= x['lookback'] + 1 
    else np.nan,
    axis=1)

# > CPU times: user 2 µs, sys: 0 ns, total: 2 µs
# > Wall time: 4.05 µs

仅供参考:您的示例中有一个错误,因为 idx[5] 的 lb_price 应该是 3 而不是 7。


推荐阅读