[英]Reshape pandas dataframe from rows to columns
我正在嘗試重塑我的數據。 乍一看,它聽起來像一個轉置,但事實並非如此。 我嘗試過融化,堆疊/取消堆疊,連接等。
用例
我希望每個唯一個體只有一行,並將所有作業歷史記錄放在列上。 對於客戶端,可以更容易地跨行讀取信息而不是讀取列。
這是數據:
import pandas as pd
import numpy as np
data1 = {'Name': ["Joe", "Joe", "Joe","Jane","Jane"],
'Job': ["Analyst","Manager","Director","Analyst","Manager"],
'Job Eff Date': ["1/1/2015","1/1/2016","7/1/2016","1/1/2015","1/1/2016"]}
df2 = pd.DataFrame(data1, columns=['Name', 'Job', 'Job Eff Date'])
df2
這就是我想要它的樣子: 所需的輸出表
.T
在groupby
def tgrp(df):
df = df.drop('Name', axis=1)
return df.reset_index(drop=True).T
df2.groupby('Name').apply(tgrp).unstack()
groupby
返回一個對象,該對象包含有關如何對原始系列或數據幀進行分組的信息。 我們可以先將df2.groupby('Name')
分配給變量(我經常這樣做),而不是使用某種類型的后續動作執行groupby
,比如說gb
。
gb = df2.groupby('Name')
在這個對象gb
我們可以調用.mean()
來獲得每個組的平均值。 或.last()
獲取每個組的最后一個元素(行)。 或.transform(lambda x: (x - x.mean()) / x.std())
在每個組中進行zscore轉換。 如果您想要在沒有預定義功能的組中執行某些操作,則仍然存在.apply()
。
groupby
對象的.apply()
與dataframe
不同.apply()
。 對於數據幀, .apply()
將可調用對象作為其參數,並將該可調用對象應用於對象中的每個列(或行)。 傳遞給該callable的對象是pd.Series
。 當您在dataframe
.apply
上下文中使用.apply
時,記住這一事實是有幫助的。 在groupby
對象的上下文中,傳遞給callable參數的對象是一個數據幀。 實際上,該數據幀是groupby
指定的組之一。
當我編寫這樣的函數以傳遞給groupby.apply
,我通常將參數定義為df
以反映它是一個數據幀。
好的,我們有:
df2.groupby('Name').apply(tgrp)
這會為每個'Name'
生成一個子數據幀,並將該子數據幀傳遞給函數tgrp
。 然后groupby
對象將已經通過tgrp
函數的所有這些組重新組合在一起。
它看起來像這樣。
我把OP最初的嘗試簡單地轉移到了心上。 但我必須先做一些事情。 我完成了:
df2[df2.Name == 'Jane'].T
df2[df2.Name == 'Joe'].T
手動組合這些(沒有groupby
):
pd.concat([df2[df2.Name == 'Jane'].T, df2[df2.Name == 'Joe'].T])
哇! 現在那很難看。 顯然, [0, 1, 2]
的索引值不與[3, 4]
。 所以讓我們重置一下。
pd.concat([df2[df2.Name == 'Jane'].reset_index(drop=True).T,
df2[df2.Name == 'Joe'].reset_index(drop=True).T])
那好多了。 但是,現在我們正在進入境內groupby
旨在處理。 所以讓它來處理它。
回到
df2.groupby('Name').apply(tgrp)
這里唯一缺少的是我們想要取消堆疊結果以獲得所需的輸出。
假設您從拆散開始:
df2 = df2.set_index(['Name', 'Job']).unstack()
>>> df2
Job Eff Date
Job Analyst Director Manager
Name
Jane 1/1/2015 None 1/1/2016
Joe 1/1/2015 7/1/2016 1/1/2016
In [29]:
df2
現在,為了簡化操作,請將多索引展平:
df2.columns = df2.columns.get_level_values(1)
>>> df2
Job Analyst Director Manager
Name
Jane 1/1/2015 None 1/1/2016
Joe 1/1/2015 7/1/2016 1/1/2016
現在,只需操縱列:
cols = []
for i, c in enumerate(df2.columns):
col = 'Job %d' % i
df2[col] = c
cols.append(col)
col = 'Eff Date %d' % i
df2[col] = df2[c]
cols.append(col)
>>> df2[cols]
Job Job 0 Eff Date 0 Job 1 Eff Date 1 Job 2 Eff Date 2
Name
Jane Analyst 1/1/2015 Director None Manager 1/1/2016
Joe Analyst 1/1/2015 Director 7/1/2016 Manager 1/1/2016
編輯
簡從來就不是導演(唉)。 以上代碼表明Jane在None
日期成為董事。 要更改結果,以便指定Jane在None
日期變為None
(這是一個品味問題),請替換
df2[col] = c
通過
df2[col] = [None if d is None else c for d in df2[c]]
這給了
Job Job 0 Eff Date 0 Job 1 Eff Date 1 Job 2 Eff Date 2
Name
Jane Analyst 1/1/2015 None None Manager 1/1/2016
Joe Analyst 1/1/2015 Director 7/1/2016 Manager 1/1/2016
這是一個可能的解決方法。 在這里,我首先創建一個正確形式的字典,並基於新字典創建一個DataFrame:
df = pd.DataFrame(data1)
dic = {}
for name, jobs in df.groupby('Name').groups.iteritems():
if not dic:
dic['Name'] = []
dic['Name'].append(name)
for j, job in enumerate(jobs, 1):
jobstr = 'Job {0}'.format(j)
jobeffdatestr = 'Job Eff Date {0}'.format(j)
if jobstr not in dic:
dic[jobstr] = ['']*(len(dic['Name'])-1)
dic[jobeffdatestr] = ['']*(len(dic['Name'])-1)
dic[jobstr].append(df['Job'].ix[job])
dic[jobeffdatestr].append(df['Job Eff Date'].ix[job])
df2 = pd.DataFrame(dic).set_index('Name')
## Job 1 Job 2 Job 3 Job Eff Date 1 Job Eff Date 2 Job Eff Date 3
## Name
## Jane Analyst Manager 1/1/2015 1/1/2016
## Joe Analyst Manager Director 1/1/2015 1/1/2016 7/1/2016
g = df2.groupby('Name').groups
names = list(g.keys())
data2 = {'Name': names}
cols = ['Name']
temp1 = [g[y] for y in names]
job_str = 'Job'
job_date_str = 'Job Eff Date'
for i in range(max([len(x) for x in g.values()])):
temp = [x[i] if len(x) > i else '' for x in temp1]
job_str_curr = job_str + str(i+1)
job_date_curr = job_date_str + str(i + 1)
data2[job_str + str(i+1)] = df2[job_str].ix[temp].values
data2[job_date_str + str(i+1)] = df2[job_date_str].ix[temp].values
cols.extend([job_str_curr, job_date_curr])
df3 = pd.DataFrame(data2, columns=cols)
df3 = df3.fillna('')
print(df3)
Name Job1 Job Eff Date1 Job2 Job Eff Date2 Job3 Job Eff Date3 0 Jane Analyst 1/1/2015 Manager 1/1/2016 1 Joe Analyst 1/1/2015 Manager 1/1/2016 Director 7/1/2016
這不是您要求的,但這是一種打印數據框的方法:
df = pd.DataFrame(data1)
for name, jobs in df.groupby('Name').groups.iteritems():
print '{0:<15}'.format(name),
for job in jobs:
print '{0:<15}{1:<15}'.format(df['Job'].ix[job], df['Job Eff Date'].ix[job]),
print
## Jane Analyst 1/1/2015 Manager 1/1/2016
## Joe Analyst 1/1/2015 Manager 1/1/2016 Director 7/1/2016
潛入@piRSquared答案....
def tgrp(df):
df = df.drop('Name', axis=1)
print df, '\n'
out = df.reset_index(drop=True)
print out, '\n'
out.T
print out.T, '\n\n'
return out.T
dfxx = df2.groupby('Name').apply(tgrp).unstack()
dfxx
以上的輸出。 為什么熊貓會重復第一組呢? 這是一個錯誤嗎?
Job Job Eff Date
3 Analyst 1/1/2015
4 Manager 1/1/2016
Job Job Eff Date
0 Analyst 1/1/2015
1 Manager 1/1/2016
0 1
Job Analyst Manager
Job Eff Date 1/1/2015 1/1/2016
Job Job Eff Date
3 Analyst 1/1/2015
4 Manager 1/1/2016
Job Job Eff Date
0 Analyst 1/1/2015
1 Manager 1/1/2016
0 1
Job Analyst Manager
Job Eff Date 1/1/2015 1/1/2016
Job Job Eff Date
0 Analyst 1/1/2015
1 Manager 1/1/2016
2 Director 7/1/2016
Job Job Eff Date
0 Analyst 1/1/2015
1 Manager 1/1/2016
2 Director 7/1/2016
0 1 2
Job Analyst Manager Director
Job Eff Date 1/1/2015 1/1/2016 7/1/2016
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.