【问题标题】:Sorting Out data based on Date and event status with Pandas使用 Pandas 根据日期和事件状态对数据进行排序
【发布时间】:2020-12-01 20:55:52
【问题描述】:

我有一个数据集,我想按小时计算每天有多少供应商在线。数据框如下所示 -

                          provider_id        event_time   final_status  rank
325  0037dfffff8b03bbdf366a263735e84b 2017-09-04 08:00:19       online     1
326  0037dfffff8b03bbdf366a263735e84b 2017-09-04 08:29:39       online     2
327  0037dfffff8b03bbdf366a263735e84b 2017-09-04 08:44:36      offline     3
328  0037dfffff8b03bbdf366a263735e84b 2017-09-04 09:06:12       online     4
330  0037dfffff8b03bbdf366a263735e84b 2017-09-04 12:23:24       online     5
331  0037dfffff8b03bbdf366a263735e84b 2017-09-04 12:23:47      offline     6
332  0037dfffff8b03bbdf366a263735e84b 2017-09-04 12:45:46       online     7
333  0037dfffff8b03bbdf366a263735e84b 2017-09-04 12:45:56      offline     8
334  0037dfffff8b03bbdf366a263735e84b 2017-09-04 17:38:31       online     9
335  0037dfffff8b03bbdf366a263735e84b 2017-09-04 17:40:51       online    10

示例输出将是这样的。在这里,如果我们查看前 3 行,我们可以看到提供者在线从 2017-09-04 08:00:192017-09-04 08:44:36。然后他一直离线直到2017-09-04 09:06:12,然后继续在线直到2017-09-04 12:23:24。这就是计算其余seconds_online 值的方式。

                      provider_id         day        start_hour  end_hour  seconds_online  
0   0037dfffff8b03bbdf366a263735e84b  2017-09-04           8         9      2657
1   0037dfffff8b03bbdf366a263735e84b  2017-09-04           9        10      3228 
2   0037dfffff8b03bbdf366a263735e84b  2017-09-04          10        11      3600
3   0037dfffff8b03bbdf366a263735e84b  2017-09-04          11        12      3600
4   0037dfffff8b03bbdf366a263735e84b  2017-09-04          12        13      2746
5   0037dfffff8b03bbdf366a263735e84b  2017-09-04          13        14      10
6   0037dfffff8b03bbdf366a263735e84b  2017-09-04          14        15      0
7   0037dfffff8b03bbdf366a263735e84b  2017-09-04          15        16      0
8   0037dfffff8b03bbdf366a263735e84b  2017-09-04          16        17      0
9   0037dfffff8b03bbdf366a263735e84b  2017-09-04          17        18      140
10  0037dfffff8b03bbdf366a263735e84b  2017-09-04          18        19      0

使用for loop,这很容易,但数据集目前有超过 2000 万行。使用for 不是优化的解决方案。它正在显示,大约需要 7 天的时间。有人可以帮忙吗??

【问题讨论】:

    标签: python python-3.x pandas numpy dataframe


    【解决方案1】:

    你可以试试这段代码,df是pandas dataframe。

    df = df.sort(['Date', 'event status'], ascending=[True, False])
    

    【讨论】:

    • 我们如何根据您的代码计算seconds_online 列?对于我已经实施的用户明智和每日明智的排名。
    【解决方案2】:

    有趣的问题。这里有一些想法,没有完整的解决方案,但也许部分有用。

    如果没有 for 循环,我无法做到这一点,这很遗憾。因此,我的策略是减少循环次数。为此,我做了一些预处理。

    第一步是摆脱不必要的“在线行”:

    import numpy as np
    import pandas as pd
    
    df['start'] = ((df.final_status.eq('online'))
                   & (df.final_status.shift(1).isin(['offline', np.NaN])))
    df['end'] = (((df.final_status.eq('offline'))
                  & (df.final_status.shift(1).eq('online')))
                 | (df.final_status.shift(-1).isna()))
    df = df[df.start != df.end].drop(columns=['final_status', 'start'])
    

    结果(我不显示不相关的列):

               event_time    end
    0 2017-09-04 08:00:19  False
    2 2017-09-04 08:44:36   True
    3 2017-09-04 09:06:12  False
    5 2017-09-04 12:23:47   True
    6 2017-09-04 12:45:46  False
    7 2017-09-04 12:45:56   True
    8 2017-09-04 17:38:31  False
    9 2017-09-04 17:40:51   True
    

    根据结果,我正在形成间隔:

    df = pd.concat([df.event_time[df.end.eq(value)].reset_index(drop=True)
                    for value in (False, True)],
                   ignore_index=True,
                   axis='columns').rename(columns={0: 'start', 1: 'end'})
    

    结果:

                    start                 end
    0 2017-09-04 08:00:19 2017-09-04 08:44:36
    1 2017-09-04 09:06:12 2017-09-04 12:23:47
    2 2017-09-04 12:45:46 2017-09-04 12:45:56
    3 2017-09-04 17:38:31 2017-09-04 17:40:51
    

    第二步是 for 循环,现在行数要少得多:

    idx = pd.date_range(df.start.min().floor('H'), df.end.max().floor('H'), freq='H')
    df_secs = pd.DataFrame({'secs': 0}, index=idx)
    df_secs.index.name = 'hours'
    for s, e in zip(df.start, df.end):
        if s.hour == e.hour:
            df_secs.loc[s.floor('H'), 'secs'] += (e - s).seconds
        else:
            df_secs.loc[s.floor('H'), 'secs'] += (s.ceil('H') - s).seconds
            hrs = pd.date_range(s.ceil('H'), e.floor('H'), freq='H', closed='left')
            df_secs.loc[hrs, 'secs'] += 3600
            df_secs.loc[e.floor('H'), 'secs'] += (e - e.floor('H')).seconds
    

    结果(print(df_secs)):

                         secs
    hours                    
    2017-09-04 08:00:00  2657
    2017-09-04 09:00:00  3228
    2017-09-04 10:00:00  3600
    2017-09-04 11:00:00  3600
    2017-09-04 12:00:00  1437
    2017-09-04 13:00:00     0
    2017-09-04 14:00:00     0
    2017-09-04 15:00:00     0
    2017-09-04 16:00:00     0
    2017-09-04 17:00:00   140
    

    我不明白为什么你有2746 对应start_hour == 1210 对应start_hour == 13?我看不出这与您提供的示例数据有何兼容。例如在12:45:56,状态为offline,下一个online 状态为17:38:31,所以start_hour == 13 不应该有任何秒数?

    正如我所说,这不是一个完整的解决方案:我猜你有很多 provider_ids,这需要一个外部 groupby 循环等。

    【讨论】:

      猜你喜欢
      • 2014-03-11
      • 2019-03-17
      • 1970-01-01
      • 2018-09-02
      • 1970-01-01
      • 2021-04-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多