【问题标题】:Add conditional dates to a column Python将条件日期添加到 Python 列
【发布时间】:2021-02-01 21:22:19
【问题描述】:

我有以下数据框:

Frequency   Date 1          Date 2        Date 3
1        2020/04/20      2021/03/04     2020/03/04
1        2020/04/20      2021/03/04     0
1        2020/04/20      2021/03/04     0
2        2020/05/26      2021/08/04     2020/08/04
2        2020/05/26      2021/08/04     0
2        2020/05/26      2021/08/04     0

我想添加月份,但以数字列为条件:因此: 如果列号为 1,则添加 12 个月。 如果列号为 2,则添加 6 个月。 如果列号为 3,则添加 4 个月。

在 Data3 列的 0 行中,如果频率列对应于 1,我需要在上一行的日期上添加一年。如果频率行是 2,则添加 6 个月。在不更改值的情况下已分配给 Data3 列(不同于 0)

Frequency   Date 1          Date 2        Date 3
1        2020/04/20      2021/03/04     2020/03/04
1        2020/04/20      2021/03/04     2021/03/04
1        2020/04/20      2021/03/04     2022/03/04
2        2020/05/26      2021/08/04     2020/08/04
2        2020/05/26      2021/08/04     2021/02/04
2        2020/05/26      2021/08/04     2021/10/04

我尝试了以下方法:

df['Date 3']=df.apply(lambda x:datetime(x['Date 1'].year+x['Frequency']-1,x['Date 2'].month,x['Date 2'].day),axis=1) 

但并不涵盖所有情况

【问题讨论】:

  • 你要把它们加到什么地方?描述并没有明确说明你要实现什么逻辑。
  • 在Data3列为0的行中,如果频率列对应1,我需要在上一行的日期上加一年。如果频率行是2,则加6个月。不更改已经分配给 Data3 列的值(不同于 0)
  • 这是一个很好的解释!您应该使用该评论更新问题,以帮助人们更好地理解。

标签: python pandas dataframe date


【解决方案1】:

我重新创建了您的数据框:

df = pd.DataFrame({'Frequency': {0: '1', 1: '1', 2: '1', 3: '2', 4: '2', 5: '2'},
 'Date 1': {0: '2020/04/20',
  1: '2020/04/20',
  2: '2020/04/20',
  3: '2020/05/26',
  4: '2020/05/26',
  5: '2020/05/26'},
 'Date 2': {0: '2021/03/04',
  1: '2021/03/04',
  2: '2021/03/04',
  3: '2021/08/04',
  4: '2021/08/04',
  5: '2021/08/04'},
 'Date 3': {0: '2020/03/04', 1: '0', 2: '0', 3: '2020/08/04', 4: '0', 5: '0'}})

然后转换dtypes:

df['Date 1'] = pd.to_datetime(df['Date 1'])
df['Date 2'] = pd.to_datetime(df['Date 2'])
df['Date 3'] = pd.to_datetime(df['Date 3'], errors='coerce')   # Makes the zeros into NaT values
df['Frequency'] = df['Frequency'].astype(int)

因此,可以减少以下步骤,以减少中间步骤,但我将保留它们以便更容易地展示流程。

创建一个列,以便我们以后进行分组。它使用新的idx 列来识别连续缺失日期组的位置。

df = df.reset_index().rename(columns={'index':'idx'})
df.loc[ df['Date 3'].isna(), 'idx'] = np.nan
df['idx'] = df['idx'].fillna(method='ffill')

print(df)

   idx  Frequency     Date 1     Date 2     Date 3
0  0.0          1 2020-04-20 2021-03-04 2020-03-04
1  0.0          1 2020-04-20 2021-03-04        NaT
2  0.0          1 2020-04-20 2021-03-04        NaT
3  3.0          2 2020-05-26 2021-08-04 2020-08-04
4  3.0          2 2020-05-26 2021-08-04        NaT
5  3.0          2 2020-05-26 2021-08-04        NaT

然后添加一个帮助列,将您的频率转换为月数,在您不需要操作 Date 3 的行中设置为零:

df['month_helper'] = 12//df['Frequency']
df.loc[ ~df['Date 3'].isna(), 'month_helper'] = 0

您要抵消Date 3 的月数实际上是连续缺失日期的偏移量的累积总和,因此我们按idx 分组并取cumsum()

df['offset'] = df.groupby('idx')['month_helper'].cumsum()

最后,我们使用该偏移量来更新Date 3 列:

df['Date 3'] = df['Date 3'].fillna(method='ffill')
df['Date 3'] = df.apply(lambda x: x['Date 3'] + pd.DateOffset(months=x['offset']), axis=1)

print(df)

   idx  Frequency     Date 1     Date 2     Date 3  month_helper  offset
0  0.0          1 2020-04-20 2021-03-04 2020-03-04             0       0
1  0.0          1 2020-04-20 2021-03-04 2021-03-04            12      12
2  0.0          1 2020-04-20 2021-03-04 2022-03-04            12      24
3  3.0          2 2020-05-26 2021-08-04 2020-08-04             0       0
4  3.0          2 2020-05-26 2021-08-04 2021-02-04             6       6
5  3.0          2 2020-05-26 2021-08-04 2021-08-04             6      12

删除辅助列会产生我认为是您想要的结果...尽管您发布的数据框的最后一个值是“2021/10/04”,但与描述不符:

df = df.drop(columns=['idx', 'month_helper','offset'])

print(df)

   Frequency     Date 1     Date 2     Date 3
0          1 2020-04-20 2021-03-04 2020-03-04
1          1 2020-04-20 2021-03-04 2021-03-04
2          1 2020-04-20 2021-03-04 2022-03-04
3          2 2020-05-26 2021-08-04 2020-08-04
4          2 2020-05-26 2021-08-04 2021-02-04
5          2 2020-05-26 2021-08-04 2021-08-04

【讨论】:

    【解决方案2】:

    这是一个直接的方法:

    数据库(抱歉,我做了一个快速的)

    df = pd.DataFrame()
    df['freq'] = [1,2,1,2,2,1,1,2,1,2]
    df['col1'] = pd.date_range("1983-03-01","1983-12-31",freq="1M")
    df['col2'] = pd.date_range("1983-03-01","1983-12-31",freq="1M")
    df['col3'] = pd.date_range("1983-03-01","1983-12-31",freq="1M")
    df.loc[3,'col3'] = None
    df.loc[4,'col3'] = None
    df.loc[5,'col3'] = None
    df.loc[8,'col3'] = None
    df
    

    还有代码:

    from pandas.tseries.offsets import DateOffset
    _condition1 = df.col3.isnull()
    _condition2 = df.freq == 1
    _condition3 = df.freq == 2
    while df.col3.isnull().any():
        df['col3'] = np.where(_condition1&_condition2,(df.col3.shift(1)+pd.DateOffset(years=1)),np.where(_condition1&_condition3,(df.col3.shift(1)+pd.DateOffset(months=6)),df.col3))
    df
    

    编辑:我已经编辑了代码,因为我首先错过了连续行中多个 NaT 的情况。感谢 Rick M

    【讨论】:

    • 如果没有地方 Date 3 是连续条目的 NaT,则您的解决方案有效,但如果连续有 2 个或更多 NaT 值(如原始示例中所示),它只会填写它在每个“组”中找到的第一个 NaT。您可以连续多次运行代码,直到列中不再有 NaT 值,但如果连续长时间运行 NaT,它会减慢一点。明确指出 freq = 1 和 2 情况的好主意;如果真的只考虑有限数量的频率,这比我对12//x 所做的更简单。
    • 你是绝对正确的。多亏了你,由于我的懒惰,我错过了那个案子。我应该像你一样复制数据库。 ;) 我编辑了我尝试在 OS 中使用简单而明确的序列编写的代码,以同时显示它的逻辑性。因为并不是所有的读者都对代码感到满意。
    • 别忘了标记你的答案结束这个帖子,其他社区成员可以从中受益。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-15
    • 2014-04-08
    • 1970-01-01
    • 2021-03-04
    • 2021-07-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多