【问题标题】:Transform wide to long but with repetition of a specific column将宽转换为长,但重复特定列
【发布时间】:2019-12-12 07:33:51
【问题描述】:

我有一个如下所示的数据框

df2 = pd.DataFrame({'pid':[1,2,3,4],'BP1Date':['12/11/2016','12/21/2016','12/31/2026',np.nan],'BP1di':[21,24,25,np.nan],'BP1sy':[123,125,127,np.nan],'BP2Date':['12/31/2016','12/31/2016','12/31/2016','12/31/2016'],'BP2di':[21,26,28,30],'BP2sy':[123,130,135,145],
                   'BP3Date':['12/31/2017','12/31/2018','12/31/2019','12/31/2116'],'BP3di':[21,31,36,np.nan],'BP3sy':[123,126,145,np.nan]})

如下图所示

我希望我的输出如下所示

这是我根据其他帖子的 SO 建议尝试的,但我无法产生或接近预期的输出

df = pd.melt(df2, id_vars='pid', var_name='col', value_name='dates')
df['col2'] = [x.split("Date")[0][:3] for x in df['col']]
df = df[df.groupby(['pid','col2'])['dates'].transform('count').ne(0)].copy()
df['col3'] = df['col2'].str.extract('(\d+)', expand=True).astype(int)
df2 = df.sort_values(by=['pid','col3'])

请注意两点

a) 对于每个日期,我有两个读数 (BP{n}di, BP{n}si)

b) 我只想在 all 3 columns 的 NA 一起删除 NA 记录(在这种情况下,pid = 4,BP1Date,BP1di,BP1sy 是 NA)。如果任何一列不是 NA,则应保留 NA,如下所示。因此我没有使用 stack(dropna=False) 而是基于 SO 帖子使用 pd.melt

如何转换输入以实现如上截图所示的输出?

根据 Answer cmets 更新截图

【问题讨论】:

    标签: python python-3.x pandas dataframe pandas-groupby


    【解决方案1】:

    使用lreshapeDataFrame.stack 进行整形,然后按Date 列按DataFrame.dropna 删除缺失值并按前3 列排序:

    a = [col for col in df2.columns if col.endswith('Date')]
    b = [col for col in df2.columns if col.endswith('di')]
    c = [col for col in df2.columns if col.endswith('sy')]
    
    df1 = (pd.lreshape(df2, {'Date':a, 'di':b, 'sy':c}, dropna=False)
           .set_index(['pid','Date'])
           .stack(dropna=False)
           .rename_axis(['pid','Date','type'])
           .reset_index(name='value')
           .dropna(subset=['Date'])
           .assign(Date = lambda x: pd.to_datetime(x['Date'], dayfirst=True))
           .sort_values(['pid','Date','type'])
           .reset_index(drop=True)
           )
    

    print (df1)
        pid       Date type  value
    0     1 2016-11-12   di   21.0
    1     1 2016-11-12   sy  123.0
    2     1 2016-12-31   di   21.0
    3     1 2016-12-31   sy  123.0
    4     1 2017-12-31   di   21.0
    5     1 2017-12-31   sy  123.0
    6     2 2016-12-21   di   24.0
    7     2 2016-12-21   sy  125.0
    8     2 2016-12-31   di   26.0
    9     2 2016-12-31   sy  130.0
    10    2 2018-12-31   di   31.0
    11    2 2018-12-31   sy  126.0
    12    3 2016-12-31   di   28.0
    13    3 2016-12-31   sy  135.0
    14    3 2019-12-31   di   36.0
    15    3 2019-12-31   sy  145.0
    16    3 2026-12-31   di   25.0
    17    3 2026-12-31   sy  127.0
    18    4 2016-12-31   di   30.0
    19    4 2016-12-31   sy  145.0
    20    4 2116-12-31   di    NaN
    21    4 2116-12-31   sy    NaN
    

    替代解决方案是在由Series.str.extractMultiIndex.from_tuples 创建的列中使用MultiIndex

    df2 = df2.set_index('pid')
    
    c = df2.columns.to_frame(name='orig')
    c = c['orig'].str.extract('(.+)(Date|di|sy)').apply(tuple, 1)
    
    df2.columns = pd.MultiIndex.from_tuples(c)
    
    df1 = (df2.stack(0)
           .set_index(['Date'], append=True)
           .reset_index(level=1, drop=True)
           .stack(dropna=False)
           .rename_axis(['pid','Date','type'])
           .reset_index(name='value')
           .dropna(subset=['Date'])
           .assign(Date = lambda x: pd.to_datetime(x['Date'], dayfirst=True))
           .sort_values(['pid','Date','type'])
           .reset_index(drop=True)
           )
    

    print (df1)
        pid       Date type  value
    0     1 2016-11-12   di   21.0
    1     1 2016-11-12   sy  123.0
    2     1 2016-12-31   di   21.0
    3     1 2016-12-31   sy  123.0
    4     1 2017-12-31   di   21.0
    5     1 2017-12-31   sy  123.0
    6     2 2016-12-21   di   24.0
    7     2 2016-12-21   sy  125.0
    8     2 2016-12-31   di   26.0
    9     2 2016-12-31   sy  130.0
    10    2 2018-12-31   di   31.0
    11    2 2018-12-31   sy  126.0
    12    3 2016-12-31   di   28.0
    13    3 2016-12-31   sy  135.0
    14    3 2019-12-31   di   36.0
    15    3 2019-12-31   sy  145.0
    16    3 2026-12-31   di   25.0
    17    3 2026-12-31   sy  127.0
    18    4 2016-12-31   di   30.0
    19    4 2016-12-31   sy  145.0
    20    4 2116-12-31   di    NaN
    21    4 2116-12-31   sy    NaN
    

    【讨论】:

    • 感谢您的回答。目前正在旅行。将很快更新结果。
    • @SSMK - 当然,还添加了替代解决方案。
    • 我仍然在真实数据中看到 NA。 (pd.lreshape(df_BP, {'Date':a, 'di':b, 'sy':c}, dropna=False).set_index(['subject_ID','Date']).stack(dropna=False) .rename_axis(['pid','Date','type']).reset_index(name='value').assign(Date = lambda x: pd.to_datetime(x['Date'], dayfirst=True)).sort_values(['pid','Date','type']).reset_index(drop=True))这是我试过的
    • @SSMK - NaN 是数字吗?还是字符串?
    • @SSMK - 真实数据是否保密?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-08
    • 2019-11-11
    • 2022-08-17
    相关资源
    最近更新 更多