如何在pandas中使用apply函数返回多行?

时间:2018-02-21 16:32:31

标签: python pandas python-3.6 pandas-groupby

上下文

我有一个包含成绩单的数据框。 df中的每一行都有唯一的ID,记录行和时间戳,每个ID在一天(或几天)内可以有多个对应关系。

以下示例代码!

我有什么:

#What I have starting out with. Df is ordered by CustomerID and Timestamp
pd.DataFrame({'AgentID': 0, 'CustomerID': 1, 'Date': ['2018-01-21', '2018-01-21', '2018-01-22', '2018-01-22'], 'Timestamp': ['2018-01-21 16:28:54', '2018-01-21 16:48:54', '2018-01-22 12:18:54', '2018-01-22 12:22:54'], 'Transcript_Line':['How can I help you?', 'I need help with this pandas problem...', 'Did you get that problem resolved?', 'Nope I still suck at pandas']})

enter image description here

我需要什么:

#This is the final result
 pd.DataFrame({'AgentID': 0, 'CustomerID': 1, 'Date': ['2018-01-21', '2018-01-22'], 'Transcript_Line': ['How can I help you?\nI need help with this pandas problem...', 'Did you get that problem resolved?\nNope I still suck at pandas']})

enter image description here

我需要组织和组合对应于同一天(按顺序)的所有成绩单(每行中的字符串)。

这是我到目前为止所尝试的 问题出在这里:

def concatConvos(x):

    if len(set(x.Date)) == 1:
        return pd.Series({'Email' : x['CustomerID'].values[0], 
                        'Date': x['Date'].values[0],
                    'Conversation' : '\n'.join(x['Transcript_Line'])})
    else:  
        rows = []
        for date in set(x.Date):
            rows.append(pd.Series({'Email': x['CustomerID'].values[0],
                                 'Date': date,
                                 'Conversation': '\n'.join(x[x.Date == date].Transcript_Line)}))
        return tuple(rows)

data3 = data2.groupby('CustomerID').apply(concatConvos)

我能够在客户只有1个通信日期(意味着他没有多次联系,第一种情况)的情况下工作。

如果我尝试处理的案例多于1,那么我最终会遇到属性错误,因为该函数正在返回多个系列对象。

有没有更简单的方法来解决这个问题?

2 个答案:

答案 0 :(得分:1)

这不是最漂亮的解决方案,也不是最有效的解决方案,但我过去曾经使用过这样的解决方案。我相信可能有一个更有效的解决方案,而不是使用循环。我会给你原始代码,然后一步一步地分解它:

transcript_join = df.groupby(['CustomerID', 'Date']).apply(lambda f: f['Transcript_Line'].values.tolist()).to_dict()

for x in transcript_join.keys():
    df.loc[(df['CustomerID']==x[0]) & (df['Date'] == x[1]), 'Combine'] = '\n'.join(transcript_join.get(x))

df.drop_duplicates(df.iloc[:,[0,1,2,5]])

# output below
    AgentID CustomerID  Date    Timestamp   Transcript_Line Combine
0   0   1   2018-01-21  2018-01-21 16:28:54 How can I help you? How can I help you?\nI need help with this pan...
2   0   1   2018-01-22  2018-01-22 12:18:54 Did you get that problem resolved?  Did you get that problem resolved?\nNope I sti...

首先,我使用变量transcript_join创建所有响应的字典。关键是客户ID,然后是日期。价值是成绩单清单。

然后我遍历键并获取字典中客户ID和日期相同的位置,并使用.join将成绩单组合在一个新列中。

最后,我删除了重复项,因为现在会有重复项,因为每个客户ID和日期对将包含相同的Combine列。我使用iloc来删除输出中不需要的列,例如原始Transcript列以及Timestamp

答案 1 :(得分:1)

您应该可以使用groupby完成此操作。这是您原来的DataFrame。我只是为了方便而将它命名为df。

df = pd.DataFrame({'AgentID': 0, 'CustomerID': 1, 'Date': ['2018-01-21', '2018-01-21', '2018-01-22', '2018-01-22'], 'Timestamp': ['2018-01-21 16:28:54', '2018-01-21 16:48:54', '2018-01-22 12:18:54', '2018-01-22 12:22:54'], 'Transcript_Line':['How can I help you?', 'I need help with this pandas problem...', 'Did you get that problem resolved?', 'Nope I still suck at pandas']})

我有点不清楚你是否需要对AgentID和CustomerID进行排序,或者只对其中一个进行排序,但希望你能看到如何修改它。

初始排序可确保Transcript_Line按顺序排列。然后,groupby在同一天查找同一AgentID和CustomerID的所有声明集。 as_index = False,为您提供输出中列的正确格式。你想要的输出是组合成绩单行,你可以用总和完成。

df.sort_values(by=['AgentID', 'CustomerID', 
    'Timestamp']).groupby(['AgentID', 'CustomerID', 
    'Date'], as_index=False)['Transcript_Line'].sum()

如果你真的需要它们之间的'\ n'字符,那么你可以通过首先将它们添加到每个抄本行,与上面做同一组,然后在组合结束时删除字符来解决这个问题。字符串。

df['Transcript_Line'] = df['Transcript_Line'] + '\n'

grouped = df.sort_values(by=['AgentID', 'CustomerID', 
    'Timestamp']).groupby(['AgentID', 'CustomerID', 
    'Date'], as_index=False)['Transcript_Line'].sum()

grouped['Transcript_Line'] = grouped['Transcript_Line'].apply(lambda 
    x: x[:-1])

resulting grouped dataframe