首页 > 解决方案 > 有没有一种基于另一个数据框中的数据创建数据框列的有效方法?

问题描述

我有两个数据框。一个包含用户订阅数据,另一个包含用户会话数据。

订阅数据示例 (df_subscriptions):

        user_id                                 created                     ended
10238   140baa7a-1641-41b5-a85b-c43dc9e12699    2021-08-13 19:37:11.373039  2021-09-12 19:37:11.373039
10237   fbfa999c-9c56-4f06-8cf9-3c5deb32d5d2    2021-08-13 15:25:07.149982  2021-09-12 15:25:07.149982
6256    a55e64b0-a783-455e-bd9d-edbb4815786b    2021-08-13 18:31:36.083681  2021-09-12 18:31:36.083681
6257    ca2c0ee1-9810-4ce7-a2ec-c036d0b8a380    2021-08-13 16:29:52.981836  2021-09-12 16:29:52.981836
7211    24378efd-e821-4a51-a3e6-39c30243a078    2021-08-13 19:58:19.434908  2021-09-12 19:58:19.434908

会话数据示例:

            user_id                                 session_start           session_duration
11960653    6f51df1a-8c2b-4ddb-9299-b36f250b05dc    2020-01-05 11:39:29.367 165.880005
80076       697e1c0a-c026-4104-b13f-1fd74eec5890    2021-01-31 02:16:33.935 22.883301
1577621     02b23671-8ce3-452b-b551-03b5ea7dce47    2021-05-18 02:07:32.589 4.283300
1286532     a506fb53-3505-44db-880a-27ad483151f0    2020-07-29 16:47:51.908 51.000000
18875432    1ea77db5-fe4a-414f-ba47-1f448175df3f    2020-10-17 04:00:35.269 360.733307

我需要计算用户在订阅处于活动状态时在服务上花费的总时间。下面的代码给了我正确/预期的结果,但在真实数据上花费了很多时间:

def sessions_during_sub (user_id, start_date, end_date):
    result = df_sessions.loc[(df_sessions.user_id == user_id)&
                             (df_sessions.session_start >= start_date)&
                             (df_sessions.session_start <= end_date)].session_duration.sum()
    return result

df_subscriptions['sessions'] = df_subscriptions.apply(lambda x: sessions_during_sub(x['user_id'], x['created'], x['ended']), axis=1)

有没有办法做到正确的熊猫方式/矢量化?关于如何真正加快速度的任何想法。

标签: pythonpandas

解决方案


创建一些示例数据:

subs = pd.DataFrame(zip(["user_0", "user_0", "user_1", "user_2"], [1900, 1920, 1950, 2000], [1910, 1930, 2000, 2020]), columns=["user_id", "created", "ended"])

  user_id  created  ended
0  user_0     1900   1910
1  user_0     1920   1930
2  user_1     1950   2000
3  user_2     2000   2020

sessions = pd.DataFrame(zip(["user_0", "user_0", "user_0", "user_2"], [1905, 1915, 1925, 2005], [1.0, 5.0, 2.0, 7.0]), columns=["user_id", "session_start", "session_duration"])

  user_id  session_start  session_duration
0  user_0           1905               1.0
1  user_0           1915               5.0
2  user_0           1925               2.0
3  user_2           2005               7.0

合并的目的是创建一个表,所有订阅和会话数据都在同一行中。这类似于sessions_during_sub在您问题的代码中应用时在两个数组中的所有行中循环检查 user_id 相等性:

merged = pd.merge(subs, sessions, on="user_id")


  user_id  created  ended  session_start  session_duration
0  user_0     1900   1910           1905               1.0
1  user_0     1900   1910           1915               5.0
2  user_0     1900   1910           1925               2.0
3  user_0     1920   1930           1905               1.0
4  user_0     1920   1930           1915               5.0
5  user_0     1920   1930           1925               2.0
6  user_2     2000   2020           2005               7.0

每个用户拥有多个订阅和多个会话在这里不是问题,您只会得到包含一些重复数据的多个结果行。然后,您可以编写一些逻辑来检查订阅范围,如下所示:

in_subscription_range = (merged.session_start >= merged.created) & (merged.session_start < merged.ended)

最后计算会话持续时间的总和,例如每个 user_id,如下所示:merged[in_subscription_range].groupby("user_id").session_duration.sum()

user_id
user_0    3.0
user_2    7.0
Name: session_duration, dtype: float64

如果您的原始数据包含时间上重叠的订阅或会话,您需要在合并之前修复它,否则您可能会多次计算持续时间。但是您的示例代码也存在同样的问题。


推荐阅读