Pandas DataFrame 重新采样中意外的 bin 数量
Unexpected number of bins in Pandas DataFrame resample
问题
我需要将 DataFrame 的长度减少到某个外部定义的整数(可以是两行、10,000 行等,但总是会减少总长度),但我也想保留结果DataFrame 代表原始。原始 DataFrame(我们称之为 df
)有一个 datetime
列(utc_time
)和一个数据值列(data_value
)。日期时间始终是连续的、不重复的,尽管间隔不均匀(即,数据可能“丢失”)。对于本例中的 DataFrame,时间戳以十分钟为间隔(当数据存在时)。
尝试次数
为了实现这一点,我的想法立即转向使用以下逻辑进行重采样:找到第一个和最后一个时间戳之间的秒差,将其除以所需的最终长度,这就是重采样因子。我在这里设置:
# Define the desired final length.
final_length = 2
# Define the first timestamp.
first_timestamp = df['utc_time'].min().timestamp()
# Define the last timestamp.
last_timestamp = df['utc_time'].max().timestamp()
# Define the difference in seconds between the first and last timestamps.
delta_t = last_timestamp - first_timestamp
# Define the resampling factor.
resampling_factor = np.ceil(delta_t / final_length)
# Set the index from the `utc_time` column so that we can resample nicely.
df.set_index('utc_time', drop=True, inplace=True)
# Do the resampling.
resamp = df.resample(f'{resampling_factor}S')
为了看resamp
,我只是循环打印:
for i in resamp:
print(i)
这产生了(我做了一些清理)以下内容:
utc_time data_value
2016-09-28 21:10:00 140.0
2016-09-28 21:20:00 250.0
2016-09-28 21:30:00 250.0
2016-09-28 21:40:00 240.0
2016-09-28 21:50:00 240.0
... ...
2018-08-06 13:00:00 240.0
2018-08-06 13:10:00 240.0
2018-08-06 13:20:00 240.0
2018-08-06 13:30:00 240.0
2018-08-06 13:40:00 230.0
[69889 rows x 1 columns])
utc_time data_value
2018-08-06 13:50:00 230.0
2018-08-06 14:00:00 230.0
2018-08-06 14:10:00 230.0
2018-08-06 14:20:00 230.0
2018-08-06 14:30:00 230.0
... ...
2020-06-14 02:50:00 280.0
2020-06-14 03:00:00 280.0
2020-06-14 03:10:00 280.0
2020-06-14 03:20:00 280.0
2020-06-14 03:30:00 280.0
[97571 rows x 1 columns])
utc_time data_value
2020-06-14 03:40:00 280.0
2020-06-14 03:50:00 280.0
2020-06-14 04:00:00 280.0
2020-06-14 04:10:00 280.0
2020-06-14 04:20:00 280.0
... ...
2020-06-15 00:10:00 280.0
2020-06-15 00:20:00 270.0
2020-06-15 00:30:00 270.0
2020-06-15 00:40:00 270.0
2020-06-15 00:50:00 280.0
[128 rows x 1 columns])
如您所见,这产生了三个箱子,而不是我预期的两个箱子。
我可以做一些不同的事情,比如改变我选择重采样因子的方式(例如,找到时间戳之间的平均时间,并将其乘以(DataFrame 的长度 / final_length
)应该会产生更保守的结果重采样因子),但在我看来,这将掩盖潜在的问题。主要是,我很想了解为什么会发生这种情况。这导致...
问题
有谁知道为什么会这样,我可以采取哪些步骤来确保我们获得所需数量的箱子?我想知道这是否是一个抵消问题 - 也就是说,虽然我们看到第一个 bin 中的第一个时间戳是 DataFrame 的第一个时间戳,但也许 pandas 实际上在此之前启动了 bin?
对于任何想在家一起玩的人,测试 DataFrame 可以是 found here 作为 .csv。将其作为 DataFrame 获取:
df = pd.read_csv('test.csv', parse_dates=[0])
总结
问题 1 和修复: 你形成垃圾箱的方式将一个额外的垃圾箱 因为用 df.resample()
创建的容器只会在一端(左侧或右侧)关闭。使用“1.”中列出的选项之一解决此问题。
问题 2 及修复: 第一个 bin 左边缘在那天的开始('2016-09-28 00:00:00' )(参见“2.”)。您可以使用 kind='period'
作为 resample()
的参数来修复它。 (参见“3.”)
1。浏览输入数据(以及我们需要什么样的 bin)
输入数据从2016-09-28 21:10:00
到2020-06-15 00:50:00
,使用你的resampling_factor
,我们得到:
In [63]: df.index.min()
Out[63]: Timestamp('2016-09-28 21:10:00')
In [64]: df.index.min() + pd.Timedelta(f'{resampling_factor}S')
Out[64]: Timestamp('2018-08-07 11:00:00')
In [65]: _ + pd.Timedelta(f'{resampling_factor}S')
Out[65]: Timestamp('2020-06-15 00:50:00')
要使用这些时间戳将数据分成 两部分 ,我们需要 bins 为
['2016-09-28 21:10:00', '2018-08-07 11:00:00')
['2018-08-07 11:00:00', '2020-06-15 00:50:00']
([
表示封闭式,(
表示开放式)
- 这里有一个问题:你不能形成两端封闭的箱子。您必须决定是否要从左侧或右侧关闭垃圾箱(参数
closed='left'|'right'
)。使用 closed='left'
你会
['2016-09-28 21:10:00', '2018-08-07 11:00:00')
['2018-08-07 11:00:00', '2020-06-15 00:50:00')
['2020-06-15 00:50:00', '2022-04-23 14:40:00')
(这里只有一个条目)
可能的修复:
- 通过添加一些时间来调整您的最后一个时间戳:
last_timestamp = (df['utc_time'].max() +
pd.Timedelta('10 minutes')).timestamp()
- 使
resampling_factor
比您最初计算的大一点。
- 只使用
df.resample
中的前两个数据帧,忽略只有一个或几个条目的第三个数据帧
选择最适合您应用的选项。
2。看看我们现在拥有的
- 从
df.resample
文档中,我们知道返回的标签是左 bin 边缘
- 如果我们查看数据,我们会看到现在有什么样的标签。
In [67]: resamp = df.resample(f'{resampling_factor}S')
In [68]: itr = iter(resamp)
In [69]: next(itr)
Out[69]:
(Timestamp('2016-09-28 00:00:00', freq='58542600S'),
data_value
utc_time
2016-09-28 21:10:00 140.0
... ...
2018-08-06 13:40:00 230.0
[69889 rows x 1 columns])
In [70]: next(itr)
Out[70]:
(Timestamp('2018-08-06 13:50:00', freq='58542600S'),
data_value
utc_time
2018-08-06 13:50:00 230.0
... ...
2020-06-14 03:30:00 280.0
[97571 rows x 1 columns])
In [71]: next(itr)
Out[71]:
(Timestamp('2020-06-14 03:40:00', freq='58542600S'),
data_value
utc_time
2020-06-14 03:40:00 280.0
... ...
2020-06-15 00:50:00 280.0
[128 rows x 1 columns])
- 垃圾箱因此
['2016-09-28 00:00:00', '2018-08-06 13:50:00')
['2018-08-06 13:50:00', '2020-06-14 03:40:00')
['2020-06-14 03:40:00', '2022-04-22 17:30:00')
(通过将 resampling_factor
添加到 bin 的开头计算的端点。)
- 我们看到第一个 bin 不是从
df['utc_time'].min
(2016-09-28 21:10:00
)开始,而是从 那天 开始(如你猜对了)
- 由于第一个 bin 在预期之前开始,我们有两个 bin 之外的数据,在 third bin 中。
3。修复起始 bin 左边缘
kind
参数可以是 'timestamp'
或 'period'
。如果将其更改为 'period'
,您将拥有以下垃圾箱(closed='left'
):
['2016-09-28 21:10:00', '2018-08-07 11:00:00')
<-- 已修复
['2018-08-07 11:00:00', '2020-06-15 00:50:00')
['2020-06-15 00:50:00', '2022-04-23 14:40:00')
(使用“1.”中给出的选项删除)
问题
我需要将 DataFrame 的长度减少到某个外部定义的整数(可以是两行、10,000 行等,但总是会减少总长度),但我也想保留结果DataFrame 代表原始。原始 DataFrame(我们称之为 df
)有一个 datetime
列(utc_time
)和一个数据值列(data_value
)。日期时间始终是连续的、不重复的,尽管间隔不均匀(即,数据可能“丢失”)。对于本例中的 DataFrame,时间戳以十分钟为间隔(当数据存在时)。
尝试次数
为了实现这一点,我的想法立即转向使用以下逻辑进行重采样:找到第一个和最后一个时间戳之间的秒差,将其除以所需的最终长度,这就是重采样因子。我在这里设置:
# Define the desired final length.
final_length = 2
# Define the first timestamp.
first_timestamp = df['utc_time'].min().timestamp()
# Define the last timestamp.
last_timestamp = df['utc_time'].max().timestamp()
# Define the difference in seconds between the first and last timestamps.
delta_t = last_timestamp - first_timestamp
# Define the resampling factor.
resampling_factor = np.ceil(delta_t / final_length)
# Set the index from the `utc_time` column so that we can resample nicely.
df.set_index('utc_time', drop=True, inplace=True)
# Do the resampling.
resamp = df.resample(f'{resampling_factor}S')
为了看resamp
,我只是循环打印:
for i in resamp:
print(i)
这产生了(我做了一些清理)以下内容:
utc_time data_value
2016-09-28 21:10:00 140.0
2016-09-28 21:20:00 250.0
2016-09-28 21:30:00 250.0
2016-09-28 21:40:00 240.0
2016-09-28 21:50:00 240.0
... ...
2018-08-06 13:00:00 240.0
2018-08-06 13:10:00 240.0
2018-08-06 13:20:00 240.0
2018-08-06 13:30:00 240.0
2018-08-06 13:40:00 230.0
[69889 rows x 1 columns])
utc_time data_value
2018-08-06 13:50:00 230.0
2018-08-06 14:00:00 230.0
2018-08-06 14:10:00 230.0
2018-08-06 14:20:00 230.0
2018-08-06 14:30:00 230.0
... ...
2020-06-14 02:50:00 280.0
2020-06-14 03:00:00 280.0
2020-06-14 03:10:00 280.0
2020-06-14 03:20:00 280.0
2020-06-14 03:30:00 280.0
[97571 rows x 1 columns])
utc_time data_value
2020-06-14 03:40:00 280.0
2020-06-14 03:50:00 280.0
2020-06-14 04:00:00 280.0
2020-06-14 04:10:00 280.0
2020-06-14 04:20:00 280.0
... ...
2020-06-15 00:10:00 280.0
2020-06-15 00:20:00 270.0
2020-06-15 00:30:00 270.0
2020-06-15 00:40:00 270.0
2020-06-15 00:50:00 280.0
[128 rows x 1 columns])
如您所见,这产生了三个箱子,而不是我预期的两个箱子。
我可以做一些不同的事情,比如改变我选择重采样因子的方式(例如,找到时间戳之间的平均时间,并将其乘以(DataFrame 的长度 / final_length
)应该会产生更保守的结果重采样因子),但在我看来,这将掩盖潜在的问题。主要是,我很想了解为什么会发生这种情况。这导致...
问题
有谁知道为什么会这样,我可以采取哪些步骤来确保我们获得所需数量的箱子?我想知道这是否是一个抵消问题 - 也就是说,虽然我们看到第一个 bin 中的第一个时间戳是 DataFrame 的第一个时间戳,但也许 pandas 实际上在此之前启动了 bin?
对于任何想在家一起玩的人,测试 DataFrame 可以是 found here 作为 .csv。将其作为 DataFrame 获取:
df = pd.read_csv('test.csv', parse_dates=[0])
总结
问题 1 和修复: 你形成垃圾箱的方式将一个额外的垃圾箱 因为用
df.resample()
创建的容器只会在一端(左侧或右侧)关闭。使用“1.”中列出的选项之一解决此问题。问题 2 及修复: 第一个 bin 左边缘在那天的开始('2016-09-28 00:00:00' )(参见“2.”)。您可以使用
kind='period'
作为resample()
的参数来修复它。 (参见“3.”)
1。浏览输入数据(以及我们需要什么样的 bin)
输入数据从2016-09-28 21:10:00
到2020-06-15 00:50:00
,使用你的resampling_factor
,我们得到:
In [63]: df.index.min()
Out[63]: Timestamp('2016-09-28 21:10:00')
In [64]: df.index.min() + pd.Timedelta(f'{resampling_factor}S')
Out[64]: Timestamp('2018-08-07 11:00:00')
In [65]: _ + pd.Timedelta(f'{resampling_factor}S')
Out[65]: Timestamp('2020-06-15 00:50:00')
要使用这些时间戳将数据分成 两部分 ,我们需要 bins 为
['2016-09-28 21:10:00', '2018-08-07 11:00:00')
['2018-08-07 11:00:00', '2020-06-15 00:50:00']
([
表示封闭式,(
表示开放式)
- 这里有一个问题:你不能形成两端封闭的箱子。您必须决定是否要从左侧或右侧关闭垃圾箱(参数
closed='left'|'right'
)。使用closed='left'
你会['2016-09-28 21:10:00', '2018-08-07 11:00:00')
['2018-08-07 11:00:00', '2020-06-15 00:50:00')
['2020-06-15 00:50:00', '2022-04-23 14:40:00')
(这里只有一个条目)
可能的修复:
- 通过添加一些时间来调整您的最后一个时间戳:
last_timestamp = (df['utc_time'].max() +
pd.Timedelta('10 minutes')).timestamp()
- 使
resampling_factor
比您最初计算的大一点。 - 只使用
df.resample
中的前两个数据帧,忽略只有一个或几个条目的第三个数据帧
选择最适合您应用的选项。
2。看看我们现在拥有的
- 从
df.resample
文档中,我们知道返回的标签是左 bin 边缘 - 如果我们查看数据,我们会看到现在有什么样的标签。
In [67]: resamp = df.resample(f'{resampling_factor}S')
In [68]: itr = iter(resamp)
In [69]: next(itr)
Out[69]:
(Timestamp('2016-09-28 00:00:00', freq='58542600S'),
data_value
utc_time
2016-09-28 21:10:00 140.0
... ...
2018-08-06 13:40:00 230.0
[69889 rows x 1 columns])
In [70]: next(itr)
Out[70]:
(Timestamp('2018-08-06 13:50:00', freq='58542600S'),
data_value
utc_time
2018-08-06 13:50:00 230.0
... ...
2020-06-14 03:30:00 280.0
[97571 rows x 1 columns])
In [71]: next(itr)
Out[71]:
(Timestamp('2020-06-14 03:40:00', freq='58542600S'),
data_value
utc_time
2020-06-14 03:40:00 280.0
... ...
2020-06-15 00:50:00 280.0
[128 rows x 1 columns])
- 垃圾箱因此
['2016-09-28 00:00:00', '2018-08-06 13:50:00')
['2018-08-06 13:50:00', '2020-06-14 03:40:00')
['2020-06-14 03:40:00', '2022-04-22 17:30:00')
(通过将resampling_factor
添加到 bin 的开头计算的端点。)
- 我们看到第一个 bin 不是从
df['utc_time'].min
(2016-09-28 21:10:00
)开始,而是从 那天 开始(如你猜对了) - 由于第一个 bin 在预期之前开始,我们有两个 bin 之外的数据,在 third bin 中。
3。修复起始 bin 左边缘
kind
参数可以是 'timestamp'
或 'period'
。如果将其更改为 'period'
,您将拥有以下垃圾箱(closed='left'
):
['2016-09-28 21:10:00', '2018-08-07 11:00:00')
<-- 已修复['2018-08-07 11:00:00', '2020-06-15 00:50:00')
['2020-06-15 00:50:00', '2022-04-23 14:40:00')
(使用“1.”中给出的选项删除)