将 pandas 数据框从行重塑为列
Reshape pandas dataframe from rows to columns
我正在尝试重塑我的数据。乍一看,这听起来像是转置,但实际上不是。我试过融化、stack/unstack、连接等
用例
我希望每个唯一的个人只有一行,并将所有工作历史记录放在列中。对于客户来说,跨行阅读信息比逐列阅读更容易。
这是数据:
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
这是我想要的样子:
Desired Output Table
这与您的要求不完全相同,但这是一种根据需要打印数据框的方法:
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
假设您从取消堆叠开始:
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
.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
returns 包含有关原始系列或数据框如何分组的信息的对象。我们可以先将 df2.groupby('Name')
分配给一个变量(我经常这样做),比如 gb
.
,而不是执行带有某种后续操作的 groupby
gb = df2.groupby('Name')
在这个对象上 gb
我们可以调用 .mean()
来获得每组的平均值。或者 .last()
获取每组的最后一个元素(行)。或者 .transform(lambda x: (x - x.mean()) / x.std())
以获得每个组内的 zscore 转换。当你想在一个没有预定义功能的组中做某事时,仍然有 .apply()
.
.apply()
对于 groupby
对象不同于对于 dataframe
。对于数据框,.apply()
将可调用对象作为其参数并将该可调用对象应用于对象中的每一列(或行)。传递给该可调用对象的对象是 pd.Series
。当您在 dataframe
上下文中使用 .apply
时,牢记这一点会很有帮助。在 groupby
对象的上下文中,传递给可调用参数的对象是数据帧。事实上,该数据框是 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)
这里唯一缺少的是我们想要拆开结果以获得所需的输出。
深入了解@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
上面的输出。为什么 pandas 重复第一组?这是一个错误吗?
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
我正在尝试重塑我的数据。乍一看,这听起来像是转置,但实际上不是。我试过融化、stack/unstack、连接等
用例
我希望每个唯一的个人只有一行,并将所有工作历史记录放在列中。对于客户来说,跨行阅读信息比逐列阅读更容易。
这是数据:
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
这是我想要的样子: Desired Output Table
这与您的要求不完全相同,但这是一种根据需要打印数据框的方法:
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
假设您从取消堆叠开始:
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
.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
returns 包含有关原始系列或数据框如何分组的信息的对象。我们可以先将 df2.groupby('Name')
分配给一个变量(我经常这样做),比如 gb
.
groupby
gb = df2.groupby('Name')
在这个对象上 gb
我们可以调用 .mean()
来获得每组的平均值。或者 .last()
获取每组的最后一个元素(行)。或者 .transform(lambda x: (x - x.mean()) / x.std())
以获得每个组内的 zscore 转换。当你想在一个没有预定义功能的组中做某事时,仍然有 .apply()
.
.apply()
对于 groupby
对象不同于对于 dataframe
。对于数据框,.apply()
将可调用对象作为其参数并将该可调用对象应用于对象中的每一列(或行)。传递给该可调用对象的对象是 pd.Series
。当您在 dataframe
上下文中使用 .apply
时,牢记这一点会很有帮助。在 groupby
对象的上下文中,传递给可调用参数的对象是数据帧。事实上,该数据框是 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)
这里唯一缺少的是我们想要拆开结果以获得所需的输出。
深入了解@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
上面的输出。为什么 pandas 重复第一组?这是一个错误吗?
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