【问题标题】:Pythonic way to transform date vector into ranges?将日期向量转换为范围的 Pythonic 方法?
【发布时间】:2019-04-17 03:22:51
【问题描述】:

我有一个熊猫数据框,每天一行,还有一些布尔列。我想将它们转换为一个 DataFrame,其中包含这些列为 True 的 ranges

一个开始DF的例子:

import pandas as pd

t = True
f = False

df = pd.DataFrame(
    {'indic': [f, f, t, t, t, f, f, f, t, f, f, t, t, t, t]},
    index=pd.date_range("2018-01-01", "2018-01-15")
)

print(df)

            indic
2018-01-01  False
2018-01-02  False
2018-01-03   True
2018-01-04   True
2018-01-05   True
2018-01-06  False
2018-01-07  False
2018-01-08  False
2018-01-09   True
2018-01-10  False
2018-01-11  False
2018-01-12   True
2018-01-13   True
2018-01-14   True
2018-01-15   True

从 2018-01-03 到 2018-01-05,然后在 2018-01-09(仅一天),然后从 2018-01-12 到 2018-01-15,此 DataFrame 的列为 True。

我在这个例子中寻找的输出是这个 DF(日期对象而不是字符串也可以,甚至是首选):

desired_result = pd.DataFrame({
    'from': ["2018-01-03", "2018-01-09", "2018-01-12"],
    'to': ["2018-01-05", "2018-01-09", "2018-01-15"]
})

print(desired_result)

         from          to
0  2018-01-03  2018-01-05
1  2018-01-09  2018-01-09
2  2018-01-12  2018-01-15

作为扩展,在后续步骤中,我希望它适用于多个列,例如:

df = pd.DataFrame(
    {
        'indic_A': [f, f, t, t, t, f, f, f, t, f, f, t, t, t, t],
        'indic_B': [f, f, f, f, f, f, f, f, t, t, t, t, t, f, f]
    },
    index=pd.date_range("2018-01-01", "2018-01-15")
)

desired_result = pd.DataFrame({
    'from': ["2018-01-03", "2018-01-09", "2018-01-12", "2018-01-09"],
    'to': ["2018-01-05", "2018-01-09", "2018-01-15", "2018-01-13"],
    'what': ["indic_A", "indic_A", "indic_A", "indic_B"]
})

print(desired_result)

         from          to     what
0  2018-01-03  2018-01-05  indic_A
1  2018-01-09  2018-01-09  indic_A
2  2018-01-12  2018-01-15  indic_A
3  2018-01-09  2018-01-13  indic_B

是否有一种 Python 式的优雅方式来执行此操作 - 甚至可能是 pandas 函数?

【问题讨论】:

  • np.where(indic[:-1]!=indic[1:]) 用于范围的结束,模拟用于开始。我能在现场想出的最好的。让我们看看是否有人有更好的想法。

标签: python pandas date datetime


【解决方案1】:

你可以试试pd.DataFrame.shift

首先制作 2 个新的上移和下移列

df['down_shift'] = df['indic'].shift()
df['up_shift'] = df['indic'].shift(-1)

df 就像

            indic down_shift up_shift
2018-01-01  False        NaN    False
2018-01-02  False      False     True
2018-01-03   True      False     True
2018-01-04   True       True     True
2018-01-05   True       True    False
2018-01-06  False       True    False
2018-01-07  False      False    False
2018-01-08  False      False     True
2018-01-09   True      False    False
2018-01-10  False       True    False
2018-01-11  False      False     True
2018-01-12   True      False     True
2018-01-13   True       True     True
2018-01-14   True       True     True
2018-01-15   True       True      NaN

这里的想法是

  • 案例 1:(indic, down_shift) = (True, False) - 开始
  • 案例 2:(indic, up_shift) = (True, False) - 结束
  • 案例 3:案例 1 和案例 2 都发生 - 开始和结束

所以我们使用了这个技巧

  • 真 - 假 = 1
  • 假 - 真 = -1
  • 真 - 真 = 0
  • 假 - 假 = 0

代码:

case_start = df['indic'] - df['down_shift']
case_end = df['indic'] - df['up_shift']

start_date_list = df[case_start == 1].index
end_date_list = df[case_end == 1].index

然后我们检查start_date_list

DatetimeIndex(['2018-01-03', '2018-01-09', '2018-01-12'], dtype='datetime64[ns]', freq=None)

然后我们检查end_date_list

DatetimeIndex(['2018-01-05', '2018-01-09'], dtype='datetime64[ns]', freq='4D')

最后一个日期不会从 True 变为 False,所以我们需要手动添加。

【讨论】:

    【解决方案2】:

    首先使用melt 进行重塑,然后通过cumsum 为唯一组创建帮助列,通过boolean indexing 仅过滤Trues 并通过函数agg 聚合agglast

    df = df.rename_axis('date').reset_index().melt('date', var_name='ind', value_name='boolean')
    df['new'] = (~df['boolean']).cumsum()
    df = (df[df['boolean']]
             .groupby('new')
             .agg({'date':['first','last'], 'ind':'first'})
             .reset_index(drop=True))
    df.columns = df.columns.map('_'.join)
    print (df)
      date_first  date_last ind_first
    0 2018-01-03 2018-01-05   indic_A
    1 2018-01-09 2018-01-09   indic_A
    2 2018-01-12 2018-01-15   indic_A
    3 2018-01-09 2018-01-13   indic_B
    

    【讨论】:

    • 魔术。非常感谢!
    猜你喜欢
    • 2019-05-15
    • 2013-08-11
    • 1970-01-01
    • 2021-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-17
    • 2010-11-27
    相关资源
    最近更新 更多