【问题标题】:Is there a faster way for finding the range of constant values in a dataframe?有没有更快的方法来查找数据框中的常量值范围?
【发布时间】:2021-06-15 11:48:09
【问题描述】:

我想找到数据框中常量值的最长持续时间。例如,给定下面的数据帧,最长持续时间应为 30 分钟(当 value = 2 时)。

import pandas as pd

d = {'date_time': ['2016-01-01 12:00:00', '2016-01-01 12:15:00',
       '2016-01-01 12:30:00', '2016-01-01 12:45:00',
       '2016-01-01 13:00:00', '2016-01-01 13:15:00',
       '2016-01-01 13:30:00', '2016-01-01 13:45:00'], 
     'value': [1,2,2,2,4,5,5,7]}

df = pd.DataFrame(data=d)

df['date_time'] = pd.to_datetime(df['date_time'])

print(df)
            date_time  value
0 2016-01-01 12:00:00      1
1 2016-01-01 12:15:00      2
2 2016-01-01 12:30:00      2
3 2016-01-01 12:45:00      2
4 2016-01-01 13:00:00      4
5 2016-01-01 13:15:00      5
6 2016-01-01 13:30:00      5
7 2016-01-01 13:45:00      7

(注意:date_time 间隔并不总是一致的。)

我设法通过查找df.value.diff().abs()==0 的索引找到它,构建了一个复杂的函数来遍历该列表并计算范围。

由于实际的数据帧比这个例子大得多,是否有快捷函数或更快的方法来获得它而无需多次迭代?

谢谢。

编辑:

在我的情况下,相同的值可以出现在其他条纹中。一个更合适的例子是

d = {'date_time': ['2016-01-01 12:00:00', '2016-01-01 12:15:00',
       '2016-01-01 12:30:00', '2016-01-01 12:45:00',
       '2016-01-01 13:00:00', '2016-01-01 13:15:00',
       '2016-01-01 13:30:00', '2016-01-01 13:45:00',
       '2016-01-01 14:00:00', '2016-01-01 14:05:00'], 
     'value': [1,2,2,2,4,5,5,7,5,5]}

df = pd.DataFrame(data=d)

df['date_time'] = pd.to_datetime(df['date_time'])

print(df)

            date_time  value
0 2016-01-01 12:00:00      1
1 2016-01-01 12:15:00      2
2 2016-01-01 12:30:00      2
3 2016-01-01 12:45:00      2
4 2016-01-01 13:00:00      4
5 2016-01-01 13:15:00      5
6 2016-01-01 13:30:00      5
7 2016-01-01 13:45:00      7
8 2016-01-01 14:00:00      5
9 2016-01-01 14:05:00      5

在本例中,当 value = 2 时,最长持续时间仍为 30 分钟。

【问题讨论】:

  • value 能否再次连续出现?
  • @KarlThornton 相同的值可能会再次出现在另一个连续中,这已经是不同的连续。

标签: python pandas dataframe datetime


【解决方案1】:

groupby + nlargest

创建一个跟踪变化的分组系列。

groupr = df.value.ne(df.value.shift()).cumsum()

创建一个映射字典,可以将groupr 键转换为df.value 列中的实际value

mapper = dict(zip(groupr, df.value))

现在我们分组并使用ptpnlargest。最后,我们使用renamemapperindex 值(即groupr 值)转换回value 值(呸,这有点令人困惑)。

df.groupby(groupr).date_time.apply(np.ptp).nlargest(1).rename(mapper)

value
2   0 days 00:30:00
Name: date_time, dtype: timedelta64[ns]

索引中的2是持续时间最长的value0 days 00:30:00 是最长的持续时间。

参考文献

np.ptp
nlargest

【讨论】:

  • 谢谢。我喜欢你的回答给出了价值和持续时间。但是,当该值再次出现在另一个条纹中时,它不适用。请参阅我更新的示例。如果您能更新您的答案,我将不胜感激。谢谢:)
  • 看看这对你是否有意义。
  • 它现在返回正确的结果。但是“应用”在我的实际数据框上似乎需要很长时间,而实际数据框要大得多。我想知道是否有更快的解决方案。谢谢。
【解决方案2】:

您可以按值列分组,并使用.size() 获取每个组的大小/长度。

>>> groups = df.groupby('value')
>>> groups.size()
value
1    1
2    3
4    1
5    2
7    1
dtype: int64

.idxmax() 将为您提供可以传递给.get_groups() 的最大组的索引

>>> groups.get_group(groups.size().idxmax())
             date_time  value
1  2016-01-01 12:15:00      2
2  2016-01-01 12:30:00      2
3  2016-01-01 12:45:00      2

然后您可以区分最后日期和第一个日期(假设它们已排序 - 如果没有,您可以对其进行排序)

>>> max_streak = groups.get_group(groups.size().idxmax())
>>> max_streak.iloc[-1].date_time - max_streak.iloc[0].date_time
Timedelta('0 days 00:30:00')

如果value 可以在其他条纹中重复,您可以使用以下方法进行分组:

groups = df.groupby((df.value != df.value.shift()).cumsum())

更新:任何连胜的最长持续时间

>>> groups = df.groupby((df.value != df.value.shift()).cumsum())
>>> last = groups.last()
>>> max_duration = (last.date_time - groups.first().date_time).nlargest(1)
>>> max_duration.iat[0]
Timedelta('0 days 00:30:00')
>>> last.loc[max_duration.index].value.iat[0]
2

【讨论】:

  • 感谢您的回答!它比我的问题更全面。 .shift() 和 cumsum 技术非常适合我的情况。
  • 实际上,上面的内容是value 中最长的连胜记录,并为您提供了持续时间 - 而不是任何连胜记录的最长持续时间。所以我可能没有正确回答你的问题。
  • 更新了我的任何连续最长持续时间的答案。
【解决方案3】:

您可以使用 pd.pivot_table 获取每个值的最小和最大日期时间值,然后计算它们之间的持续时间并提取最长的。

import pandas as pd
import numpy as np

d = {'date_time': ['2016-01-01 12:00:00', '2016-01-01 12:15:00',
       '2016-01-01 12:30:00', '2016-01-01 12:45:00',
       '2016-01-01 13:00:00', '2016-01-01 13:15:00',
       '2016-01-01 13:30:00', '2016-01-01 13:45:00'], 
     'value': [1,2,2,2,4,5,5,7]}

df = pd.DataFrame(data=d)

df['date_time'] = pd.to_datetime(df['date_time'])

df_pivot = pd.pivot_table(df, index='value', values='date_time', aggfunc=[np.min,np.max])

df_pivot['duration'] = df_pivot.iloc[:, 1] - df_pivot.iloc[:, 0]

print(df_pivot[df_pivot['duration'] == max(df_pivot['duration'])])

【讨论】:

  • 对不起,我的问题没有表达清楚。请参阅我更新的示例。该值实际上可以出现在其他条纹中,但这不应该被计算在内。谢谢。
猜你喜欢
  • 2022-01-27
  • 2022-12-01
  • 2013-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-16
  • 2019-10-09
  • 1970-01-01
相关资源
最近更新 更多