为了账号安全,请及时绑定邮箱和手机立即绑定

将日期向量转换为范围的 Pythonic 方法?

将日期向量转换为范围的 Pythonic 方法?

明月笑刀无情 2021-08-17 16:00:33
我有一个每天一行的 Pandas DataFrame 和一些布尔列。我想将它们转换成一个 DataFrame 来保存这些列为True的范围。启动 DF 的示例:import pandas as pdt = Truef = Falsedf = 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)            indic2018-01-01  False2018-01-02  False2018-01-03   True2018-01-04   True2018-01-05   True2018-01-06  False2018-01-07  False2018-01-08  False2018-01-09   True2018-01-10  False2018-01-11  False2018-01-12   True2018-01-13   True2018-01-14   True2018-01-15   True这个 DataFrame 的列从 2018-01-03 到 2018-01-05 是 True,然后是 2018-01-09(只有一天),然后是从 2018-01-12 到 2018-01-15。我在这个例子中寻找的输出是这个 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          to0  2018-01-03  2018-01-051  2018-01-09  2018-01-092  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     what0  2018-01-03  2018-01-05  indic_A1  2018-01-09  2018-01-09  indic_A2  2018-01-12  2018-01-15  indic_A3  2018-01-09  2018-01-13  indic_B有没有一种pythonic的、优雅的方式来做到这一点——甚至可能是一个pandas函数?
查看完整描述

2 回答

?
慕码人2483693

TA贡献1860条经验 获得超9个赞

使用melt了重塑第一,然后创建帮手唯一的组列通过cumsum,过滤器只有True人民共同boolean indexing和聚合agg的功能first和last:


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


查看完整回答
反对 回复 2021-08-17
?
BIG阳

TA贡献1859条经验 获得超6个赞

你可以试试 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,因此我们需要手动添加它。


查看完整回答
反对 回复 2021-08-17
  • 2 回答
  • 0 关注
  • 134 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信