Python 连接数据帧中早期行的值

Python 连接数据帧中早期行的值,python,pandas,Python,Pandas,我有一个有点奇怪的问题 我有一个源数据框,它有三列:Customer、Date和Item。我想添加一个包含项目历史记录的新列,该列是该客户在较早(由日期定义)行中的所有项目的数组。例如,给定此源数据帧: Customer Date Item Bert 01/01/2019 Bread Bert 15/01/2019 Cheese Bert 20/01/2019 Apples Bert 22/01/2019 Pears Ernie 01/01/201

我有一个有点奇怪的问题

我有一个源数据框,它有三列:Customer、Date和Item。我想添加一个包含项目历史记录的新列,该列是该客户在较早(由日期定义)行中的所有项目的数组。例如,给定此源数据帧:

Customer    Date    Item
Bert    01/01/2019  Bread
Bert    15/01/2019  Cheese
Bert    20/01/2019  Apples
Bert    22/01/2019  Pears
Ernie   01/01/2019  Buzz Lightyear
Ernie   15/01/2019  Shellfish
Ernie   20/01/2019  A pet dog
Ernie   22/01/2019  Yoghurt
Steven  01/01/2019  A golden toilet
Steven  15/01/2019  Dominoes
我想创建此历史记录功能:

Customer    Date    Item    Item History
Bert    01/01/2019  Bread   NaN
Bert    15/01/2019  Cheese  [Bread]
Bert    20/01/2019  Apples  [Bread, Cheese]
Bert    22/01/2019  Pears   [Bread, Cheese, Apples]
Ernie   01/01/2019  Buzz Lightyear  NaN
Ernie   15/01/2019  Shellfish   [Buzz Lightyear]
Ernie   20/01/2019  A pet dog   [Buzz Lightyear, Shellfish]
Ernie   22/01/2019  Yoghurt [Buzz Lightyear, Shellfish, A pet dog]
Steven  01/01/2019  A golden toilet NaN
Steven  15/01/2019  Dominoes    [A golden toilet]
我可以执行以下操作以按日期获取历史记录:


因此,如果一个客户在一天内购买了多个项目,它们都列在一个数组中,如果一个客户只购买了一个在其自己的数组中的项目,但我不知道如何将它们与前面的行连接起来。

使用自定义lambda函数,最后将空列表替换为
NaN
s:

f = lambda x: [x[:i].tolist() for i in range(len(x))]
df['Item History'] = df.groupby('Customer')['Item'].transform(f)
另一个列表理解解决方案:

df['Item History'] = [x.Item[:i].tolist() for j, x in df.groupby('Customer') 
                                          for i in range(len(x))]

df.loc[~df['Item History'].astype(bool), 'Item History']= np.nan


我使用@jezrael的答案花了很长时间,但由于数据集的大小,结果太慢了。为了改进这一点,我创建了一个执行相同操作的函数:

def buildItemHistoryPy(customers, items):

    output = []
    customer_ix = 0

    for i in range(len(customers)):
        if customers[i] == customers[i-1]:
            output.append(items[customer_ix:i])
        else:
            customer_ix = i
            output.append(items[customer_ix:i])

    return output

df['Item History'] = buildItemHistoryPy(df.CustomerAccountNum.values, df.ItemId.values)
我的意图是将其用作Cython函数的基础(我希望它会快得多),但令我惊讶的是,裸python函数本身就快得多。不管怎样,我还是继续说:

%%cython
import numpy as np
cimport numpy as np

cpdef list buildItemHistoryCy(np.ndarray customers, np.ndarray items):

    cdef list output = []
    cdef int customer_ix = 0

    for i in range(len(customers)):
        if customers[i] == customers[i-1]:
            output.append(items[customer_ix:i])
        else:
            customer_ix = i
            output.append(items[customer_ix:i])

    return output
结果基本上是两种功能中的任何一种都更快,但Cython在一定程度上是最好的:

%timeit -n5 df['Item History1'] = [x.ItemID[:i].tolist() for j, x in df.groupby('CustomerAccountNum') for i in range(len(x))]
%timeit -n5 df['Item History2'] = buildItemHistoryPy(df.CustomerAccountNum.values, df.ItemID.values)
%timeit -n5 df['Item History3'] = buildItemHistoryCy(df.CustomerAccountNum.values, df.ItemID.values)

7.46 s ± 346 ms per loop (mean ± std. dev. of 7 runs, 5 loops each)
53.5 ms ± 2.16 ms per loop (mean ± std. dev. of 7 runs, 5 loops each)
23.6 ms ± 2.53 ms per loop (mean ± std. dev. of 7 runs, 5 loops each)

我的要求已略有改变,因此空列表的空值不再需要。如果是,则必须更改函数,以便您添加
项[customer_ix:i].tolist()

请将示例数据添加为可复制和粘贴的代码,而不是图片。另请参见和@Erfan yeah fixed。我忘了那有多烦人;如果一个客户每天有多个销售额,这实际上不太合适。我在这里不够具体(应该在样本数据中包含同一天的问题);连接只需要根据日期列说明严格较早的行。换句话说,排序顺序中较早但与当前行日期相同的行不应包含在串联中。“你知道如何调整你的答案以适应这种情况吗?@DanScally-我认为最好的办法是创造新的问题,可能吗?”?是否可能更改的样本数据?非常感谢。
%%cython
import numpy as np
cimport numpy as np

cpdef list buildItemHistoryCy(np.ndarray customers, np.ndarray items):

    cdef list output = []
    cdef int customer_ix = 0

    for i in range(len(customers)):
        if customers[i] == customers[i-1]:
            output.append(items[customer_ix:i])
        else:
            customer_ix = i
            output.append(items[customer_ix:i])

    return output
%timeit -n5 df['Item History1'] = [x.ItemID[:i].tolist() for j, x in df.groupby('CustomerAccountNum') for i in range(len(x))]
%timeit -n5 df['Item History2'] = buildItemHistoryPy(df.CustomerAccountNum.values, df.ItemID.values)
%timeit -n5 df['Item History3'] = buildItemHistoryCy(df.CustomerAccountNum.values, df.ItemID.values)

7.46 s ± 346 ms per loop (mean ± std. dev. of 7 runs, 5 loops each)
53.5 ms ± 2.16 ms per loop (mean ± std. dev. of 7 runs, 5 loops each)
23.6 ms ± 2.53 ms per loop (mean ± std. dev. of 7 runs, 5 loops each)