You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何更快从DataFrame生成指定结构的联系人统计字典?

高效将包含嵌套参会者的DataFrame转换为联系人统计字典

问题背景

我有如下结构的DataFrame:

eventattendeesduration
meeting[{"id":1, "email": "email1"}, {"id":2, "email": "email2"}]3600
lunch[{"id":2, "email": "email2"}, {"id":3, "email": "email3"}]7200

需要转换为以下结构的字典:

{
    "email1": {
       'num_events_with': 1,
       'duration_of_events': 3600,
    },
    "email2": {
       'num_events_with': 2,
       'duration_of_events': 10800,
    },
    "email3": {
       'num_events_with': 1,
       'duration_of_events': 7200,
    },
}

实际场景中DataFrame有数千行,当前实现速度过慢,代码如下:

# need to sort because I use diff() later
df.sort_values(by='startTime', ascending=True, inplace=True)

# a list of all contacts (emails) that have been in events with user
contacts = contacts_in_events
contact_info_dict = {}
df['attendees_str'] = df['attendees'].astype(str)

for contact in contacts:
    temp_df = df[df['attendees_str'].str.contains(contact)]
    duration_of_events = temp_df['duration'].sum()
    num_events_with = len(temp_df.index)

    contact_info_dict[contact] = {
        'duration_of_events': duration_of_events,
        'num_events_with': num_events_with
    }

实际DataFrame的单条记录样例(to_dict('records')输出):

{
 'creator': {
            'displayName': None,
            'email': 'creator of event',
            'id': None,
            'self': None
  },
  'start': {
            'date': None,
            'dateTime': '2022-09-13T12:30:00-04:00',
            'timeZone': 'America/Toronto'
  },
  'end': {
          'date': None,
          'dateTime': '2022-09-13T13:00:00-04:00',
          'timeZone': 'America/Toronto'
   },
  'attendees': [
       {
        'comment': None,
        'displayName': None,
        'email': 'email1@email.com',
        'responseStatus': 'accepted'
       },
       {
        'comment': None,
        'displayName': None,
        'email': 'email2@email.com',
        'responseStatus': 'accepted'
       }
  ],
  'summary': 'One on One Meeting',
  'description': '...',
  'calendarType': 'work',
  'startTime': Timestamp('2022-09-13 16:30:00+0000', tz='UTC'),
  'endTime': Timestamp('2022-09-13 17:00:00+0000', tz='UTC'),
  'eventDuration': 1800.0,
  'dowStart': 1.0,
  'endStart': 1.0,
  'weekday': True,
  'startTOD': 59400,
  'endTOD': 61200,
  'day': Period('2022-09-13', 'D')
}

高效解决方案

当前方法速度慢的核心原因是循环遍历每个联系人并做字符串匹配,数据量大时会产生O(N*M)的时间复杂度(N为联系人数量,M为DataFrame行数)。以下是两种更高效的实现方式:

方法1:利用explode展开参会者列表

通过展开嵌套的参会者列表,结合Pandas分组聚合完成统计,全程用向量化操作替代循环:

# 1. 从参会者列表中提取邮箱,生成新列
df['emails'] = df['attendees'].apply(lambda x: [attendee['email'] for attendee in x])

# 2. 将邮箱列表展开为单行对应单个参会者的格式
exploded_df = df.explode('emails')

# 3. 按邮箱分组,统计事件数和总时长
aggregated = exploded_df.groupby('emails').agg(
    num_events_with=('emails', 'count'),
    duration_of_events=('eventDuration', 'sum')  # 对应实际数据中的时长列名
).reset_index()

# 4. 转换为目标字典格式
contact_info_dict = {
    row['emails']: {
        'num_events_with': row['num_events_with'],
        'duration_of_events': row['duration_of_events']
    } for _, row in aggregated.iterrows()
}

方法2:单次遍历字典累加(内存友好)

如果DataFrame过大,explode会占用过多内存,可采用一次遍历完成统计,时间复杂度仅O(M):

contact_info_dict = {}

for _, row in df.iterrows():
    current_duration = row['eventDuration']
    for attendee in row['attendees']:
        email = attendee['email']
        if email not in contact_info_dict:
            contact_info_dict[email] = {
                'num_events_with': 0,
                'duration_of_events': 0
            }
        contact_info_dict[email]['num_events_with'] += 1
        contact_info_dict[email]['duration_of_events'] += current_duration

关键优化点

  • 跳过字符串匹配:直接从嵌套字典中提取邮箱,精准且避免不必要的字符串操作开销。
  • 用向量化操作或单次遍历替代多轮循环,将时间复杂度从O(N*M)降低到O(M)。
  • 若需保留排序逻辑,提前对DataFrame排序即可,不影响统计效率。

内容的提问来源于stack exchange,提问作者CofeeBean42

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 10:40:41