簡體   English   中英

如何將兩列 Pandas Dataframe 移動和堆疊為一列?

[英]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

我想堆ETHNICSEX下的值一起列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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM