如何在 pandas 数据框中划分时间

问题描述 投票:0回答:5

我正在尝试使用 pandas 数据帧来分析几周内测量值“X”的平均每日波动,但是事实证明,时间戳/日期时间等处理起来特别困难。花了好几个小时试图解决这个问题,我的代码变得越来越混乱,我认为我离解决方案还没有更近,希望这里有人能引导我走向正确的方向。

我在不同时间和不同日期测量了 X,将每日结果保存到具有以下形式的数据框中:

    Timestamp(datetime64)         X 

0    2015-10-05 00:01:38          1
1    2015-10-05 06:03:39          4 
2    2015-10-05 13:42:39          3
3    2015-10-05 22:15:39          2

随着测量时间每天都在变化,我决定使用分箱来组织数据,然后计算出每个箱的平均值和 STD,然后我可以绘制它们。我的想法是创建一个包含 bin 和测量值 X 平均值的最终数据框,“观察”列只是为了帮助理解:

        Time Bin       Observations     <X>  

0     00:00-05:59      [ 1 , ...]       2.3
1     06:00-11:59      [ 4 , ...]       4.6
2     12:00-17:59      [ 3 , ...]       8.5
3     18:00-23:59      [ 2 , ...]       3.1

然而,我遇到了时间、日期时间、datetime64、timedelta 和使用

pd.cut
pd.groupby
进行分箱之间不兼容的困难,基本上我觉得我在黑暗中进行刺击,不知道“正确” ' 解决这个问题的方法。我能想到的唯一解决方案是通过数据帧进行逐行迭代,但我真的很想避免这样做。

python pandas datetime pandas-groupby
5个回答
21
投票

如何
bin
数据

import pandas as pd
import numpy as np  # for test data
import random  # for test data

# setup a sample dataframe; creates 1.5 months of hourly observations
np.random.seed(365)
random.seed(365)
data = {'date': pd.bdate_range('2020-09-21', freq='h', periods=1100).tolist(),
        'x': np.random.randint(10, size=(1100))}
df = pd.DataFrame(data)

# the date column of the sample data is already in a datetime format
# if the date column is not a datetime, then uncomment the following line
# df.date= pd.to_datetime(df.date)

# define the bins
bins = [0, 6, 12, 18, 24]

# add custom labels if desired
labels = ['00:00-05:59', '06:00-11:59', '12:00-17:59', '18:00-23:59']

# add the bins to the dataframe
df['Time Bin'] = pd.cut(df.date.dt.hour, bins, labels=labels, right=False)

df.head()

                  date  x     Time Bin
0  2020-09-21 00:00:00  2  00:00-05:59
1  2020-09-21 01:00:00  4  00:00-05:59
2  2020-09-21 02:00:00  1  00:00-05:59
3  2020-09-21 03:00:00  5  00:00-05:59
4  2020-09-21 04:00:00  2  00:00-05:59

df.tail()

                    date  x     Time Bin
1095 2020-11-05 15:00:00  2  12:00-17:59
1096 2020-11-05 16:00:00  3  12:00-17:59
1097 2020-11-05 17:00:00  1  12:00-17:59
1098 2020-11-05 18:00:00  2  18:00-23:59
1099 2020-11-05 19:00:00  2  18:00-23:59

分组依据
'Time Bin'

# groupby Time Bin and aggregate a list for the observations, and mean
dfg = df.groupby('Time Bin', as_index=False)['x'].agg([list, 'mean'])

# change the column names, if desired
dfg.columns = ['X Observations', 'X mean']

dfg

                      X Observations    X mean
Time Bin                                 
00:00-05:59  [2, 4, 1, 5, 2, 2, ...]  4.416667
06:00-11:59  [9, 8, 4, 0, 3, 3, ...]  4.760870
12:00-17:59  [7, 7, 7, 0, 8, 4, ...]  4.384058
18:00-23:59  [3, 2, 6, 2, 6, 8, ...]  4.459559

8
投票

每当我按时间范围对时间序列数据进行分类时,这似乎就是您在这里所做的,我只是创建一个“一天中的小时”列并对其进行切片。另外,我通常将索引设置为日期时间值......尽管这里没有必要。

# assuming your "timestamp" column is labeled ts: 
df['hod'] = [r.hour for r in df.ts]

# now you can calculate stats for each bin
ave = df[ (df.hod>=0) & (df.hod<6) ].mean()

我认为这里有一种使用 df.resample 的方法,但是由于时间序列中定义不明确的起点/终点,我认为这可能比上述方法需要更多的关注。

这符合您想要的吗?


2
投票

不确定我有最好的答案,但我认为它无论如何都是有效的。
首先,我会使用这篇文章将

datetime64
转换为
datetime
日期时间、时间戳和 datetime64 之间的转换

然后,如果我们假设你的第一列有

datetime
并且被称为
TimeStamp
,我会做这样的事情:

def bin_f(x):
    if x.time() < datetime.time(6):
        return "00:00-05:59"
    elif x.time() < datetime.time(12):
        return "06:00-11:59"
    elif x.time() < datetime.time(18):
        return "12:00-17:59"
    else:
        return "18:00-23:59"

df["Bin"] = df["TimeStamp"].apply(bin_f)
grouped = df.groupby("Bin")
grouped['X'].agg(np.std)

其中

X
是您的专栏名称。


1
投票

虽然这是一个旧线程,但添加了另一个方法。 使用 pandas 重新采样方法可以用更少的代码行给出所需的结果。

data = {'date': pd.bdate_range('2020-09-21', freq='h', periods=24).tolist(),
    'x': np.random.randint(10, size=(24))}
df = pd.DataFrame(data)
df
# This line will resample data by 6H timeframe
dfrs=df.resample('6H',on='date').agg({'x':[list,'mean']})
dfrs
                        X Observations    X mean
date                                             
2020-09-21 00:00:00  [2, 4, 1, 5, 2, 2]  2.666667
2020-09-21 06:00:00  [9, 8, 4, 0, 3, 3]  4.500000
2020-09-21 12:00:00  [7, 7, 7, 0, 8, 4]  5.500000
2020-09-21 18:00:00  [3, 2, 6, 2, 6, 8]  4.500000

0
投票

我发现Mathiou的回复对我的目的有帮助,但修改如下:

def bin_f(x):
    h = x.time()
    if h < 6:
        return "00:00-05:59"
    elif h < 12:
        return "06:00-11:59"
    elif h < 18:
        return "12:00-17:59"
    else:
        return "18:00-23:59"
© www.soinside.com 2019 - 2024. All rights reserved.