[英]How to shift and stack two columns of pandas Dataframe into one column?
我有一個下面提到的數據框:
ETHNIC SEX USUBJID
0 HISPANIC OR LATINO F 16
1 HISPANIC OR LATINO M 8
2 HISPANIC OR LATINO Total__##!!?? 24
3 NOT HISPANIC OR LATINO F 25
4 NOT HISPANIC OR LATINO M 18
5 NOT HISPANIC OR LATINO Total__##!!?? 43
6 Total__##!!?? F 41
7 Total__##!!?? M 26
8 Total__##!!?? Total__##!!?? 67
只需將上面的數據df = pd.read_clipboard('\\s\\s+')
復制到剪貼板並執行df = pd.read_clipboard('\\s\\s+')
來加載上面的數據框。
我正在嘗試將其轉換為以下數據框:
stacked USUBJID
0 HISPANIC OR LATINO NaN <-----
0 F 16
1 M 8
2 Total__##!!?? 24
0 NOT HISPANIC OR LATINO NaN <-----
3 F 25
4 M 18
5 Total__##!!?? 43
0 Total__##!!?? NaN <-----
6 F 41
7 M 26
8 Total__##!!?? 67
我想堆ETHNIC
和SEX
下的值一起列ETHNIC
列中的每個唯一值的ETHNIC
柱。
我正在嘗試這樣的事情,這可行,但我認為這不是一個強大的解決方案。 我試圖將它拆分為n
(其中 n 是EHTNIC
列中唯一值的數量)列表中的數據幀,每個數據幀切片都有一個空行,然后最后連接數據幀切片的列表並執行其余操作作品。
cols = ['ETHNIC', 'SEX']
results = []
for v in df[cols[0]].unique():
results.append(pd.DataFrame([[None]*df.shape[1]], columns=df.columns))
results.append(df[df[cols[0]].eq(v)])
results = pd.concat(results)
results[cols[0]] = results[cols[0]].bfill()
results['stacked'] = results.apply(lambda x: x['SEX'] if x['SEX'] else x['ETHNIC'], axis=1)
results = results.drop(columns=cols)[['stacked', 'USUBJID']]
首先使用pandas.DataFrame.groupby
對“ETHNIC”進行pandas.DataFrame.groupby
。
每個組將包含一個 DataFrame 並僅保留 ['SEX', 'USUBJID'] 列,只是“SEX”的名稱不同,使用pandas.DataFrame.rename
進行pandas.DataFrame.rename
。
使用組名d.name
添加標頭並使用pandas.concat
與組 DataFrame pandas.concat
最后,使用pandas.DataFrame.reset_index
刪除操作產生的 MultiIndex 的第一級
(df.groupby('ETHNIC')
.apply(lambda d: pd.concat([pd.DataFrame([{'stacked': d.name, 'USUBJID': np.NaN}]),
d[['SEX', 'USUBJID']].rename(columns={'SEX': 'stacked'})
]))
.reset_index(level=0, drop=True)
)
輸出:
stacked USUBJID
0 HISPANIC OR LATINO NaN
0 F 16.0
1 M 8.0
2 Total__##!!?? 24.0
0 NOT HISPANIC OR LATINO NaN
3 F 25.0
4 M 18.0
5 Total__##!!?? 43.0
0 Total__##!!?? NaN
6 F 41.0
7 M 26.0
8 Total__##!!?? 67.0
讓我們嘗試reshape
from collections import defaultdict
def reshape():
data = defaultdict(list)
for k, g in df.groupby('ETHNIC'):
data['stacked'] += [k, *g['SEX']]
data['USUBJID'] += [np.nan, *g['USUBJID']]
return data
pd.DataFrame(reshape())
stacked USUBJID
0 HISPANIC OR LATINO NaN
1 F 16.0
2 M 8.0
3 Total__##!!?? 24.0
4 NOT HISPANIC OR LATINO NaN
5 F 25.0
6 M 18.0
7 Total__##!!?? 43.0
8 Total__##!!?? NaN
9 F 41.0
10 M 26.0
11 Total__##!!?? 67.0
您可以使用groupby().apply()
import io
df = pd.read_csv(io.StringIO(""" ETHNIC SEX USUBJID
0 HISPANIC OR LATINO F 16
1 HISPANIC OR LATINO M 8
2 HISPANIC OR LATINO Total__##!!?? 24
3 NOT HISPANIC OR LATINO F 25
4 NOT HISPANIC OR LATINO M 18
5 NOT HISPANIC OR LATINO Total__##!!?? 43
6 Total__##!!?? F 41
7 Total__##!!?? M 26
8 Total__##!!?? Total__##!!?? 67"""), sep="\s\s+", engine="python")
df.groupby("ETHNIC", as_index=False).apply(
lambda d: pd.concat(
[d.iloc[0,].to_frame().T.assign(USUBJID=np.nan),
d.assign(ETHNIC=d.SEX),
]
).drop(columns="SEX")
).reset_index(drop=True)
種族的 | USUBJID | |
---|---|---|
0 | 西班牙裔或拉丁裔 | 南 |
1 | F | 16 |
2 | 米 | 8 |
3 | 全部的__##!!?? | 24 |
4 | 不是西班牙裔或拉丁裔 | 南 |
5 | F | 25 |
6 | 米 | 18 |
7 | 全部的__##!!?? | 43 |
8 | 全部的__##!!?? | 南 |
9 | F | 41 |
10 | 米 | 26 |
11 | 全部的__##!!?? | 67 |
主要是為了好玩,這是基於@Shubham Sharma 的答案的另一個選項,不需要 defaultdict。 甚至可以刪除對numpy的依賴(見最后的替代方案)
它只使用pandas.DataFrame構造函數和pandas.concat 。
import numpy as np
pd.concat([pd.DataFrame({'stacked': np.append(k, g['SEX']),
'USUBJID': np.append(np.NaN, g['USUBJID']),
})
for k,g in df.groupby('ETHNIC')
])
輸出:
stacked USUBJID
0 HISPANIC OR LATINO NaN
1 F 16.0
2 M 8.0
3 Total__##!!?? 24.0
0 NOT HISPANIC OR LATINO NaN
1 F 25.0
2 M 18.0
3 Total__##!!?? 43.0
0 Total__##!!?? NaN
1 F 41.0
2 M 26.0
3 Total__##!!?? 67.0
沒有 numpy 的替代方案:
pd.concat([pd.DataFrame({'stacked': [k]+g['SEX'].to_list(),
'USUBJID': [None]+g['USUBJID'].to_list(),
})
for k,g in df.groupby('ETHNIC')
])
# use `total` as a counter
(d.assign(total=lambda df: pd.Series(np.where(df.SEX.str.startswith("Total"),
df.index, np.nan)).bfill()
)
.melt(['USUBJID', 'total'], ignore_index = False)
.sort_index()
.assign(temp = lambda df: df.variable.str.startswith("ETH").groupby(df.total).cumsum(),
USUBJID = lambda df: np.where(df.variable.str.startswith("ETH"),
np.nan, df.USUBJID))
# keep only first row for `ETHNIC`
.query("variable == 'ETHNIC' and temp == 1 or variable=='SEX' and temp >= 1")
.drop(columns=['variable','total', 'temp'])
)
USUBJID value
0 NaN HISPANIC OR LATINO
0 16.0 F
1 8.0 M
2 24.0 Total__##!!??
3 NaN NOT HISPANIC OR LATINO
3 25.0 F
4 18.0 M
5 43.0 Total__##!!??
6 NaN Total__##!!??
6 41.0 F
7 26.0 M
8 67.0 Total__##!!??
就個人而言,其他答案更簡單,更容易理解
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.