从宽到长变换,但重复特定列

2024-04-25 20:49:27 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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]})

如下所示

enter image description here

我希望我的输出如下所示

enter image description here

这是我尝试了这么多来自其他职位的建议,但我无法产生或接近预期的产出

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)我只想在NA为all 3 columns时删除NA记录(在本例中,对于pid=4,BP1Date,BP1di,BP1sy为NA)。如果任何列不是NA,则应保留NA,如下所示。因此,我没有使用stack(dropna=False),而是基于SO posts使用pd.melt

如何转换输入以实现屏幕截图中所示的输出

根据答案评论更新截图

enter image description here


Tags: namedfnpcolnanpidcol2pd
1条回答
网友
1楼 · 发布于 2024-04-25 20:49:27

使用lreshape^{}进行重塑,然后按Date列按^{}删除缺少的值,并按前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

另一种解决方案是在由^{}^{}创建的列中使用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

相关问题 更多 >

    热门问题