Pandas- 将列旋转为(条件)聚合字符串
Pandas- pivoting column into (conditional) aggregated string
假设我有以下数据集,变成了一个数据框:
data = [
['Job 1', datetime.date(2019, 6, 9), 'Jim', 'Tom'],
['Job 1', datetime.date(2019, 6, 9), 'Bill', 'Tom'],
['Job 1', datetime.date(2019, 6, 9), 'Tom', 'Tom'],
['Job 1', datetime.date(2019, 6, 10), 'Bill', None],
['Job 2', datetime.date(2019,6,10), 'Tom', 'Tom']
]
df = pd.DataFrame(data, columns=['Job', 'Date', 'Employee', 'Manager'])
这会产生一个如下所示的数据框:
Job Date Employee Manager
0 Job 1 2019-06-09 Jim Tom
1 Job 1 2019-06-09 Bill Tom
2 Job 1 2019-06-09 Tom Tom
3 Job 1 2019-06-10 Bill None
4 Job 2 2019-06-10 Tom Tom
我要生成的是每个独特 Job/Date 组合的一个枢轴,其中有一列用于经理,一列用于字符串,其中以逗号分隔,非经理员工。假设有几件事:
- 所有员工姓名都是唯一的(实际上我将使用唯一的员工 ID 而不是姓名),经理也是 "employees",因此永远不会出现员工和经理共享姓名的情况相同 name/id,但不同的个体。
- 一个工作组可以有经理,也可以没有(参见 id 为 3 的行,例如没有)
- 经理也将始终被列为员工(参见 ID 为 2 或 4 的行)
- 一个工作可以有一个经理,没有额外的员工(见第 id 4 行)
我希望生成的数据框看起来像:
Job Date Manager Employees
0 Job 1 2019-06-09 Tom Jim, Bill
1 Job 1 2019-06-10 None Bill
2 Job 2 2019-06-10 Tom None
这引出了我的问题:
- 有没有办法在 pandas 主元中执行类似聚合的 ','.join?
- 有没有办法使此聚合成为条件(排除管理器列中的 name/id)
我怀疑 1) 是可能的,2) 可能更难。如果 2) 是否定的,我可以稍后在我的代码中以其他方式绕过它。
怎么样
df.groupby(["Job","Date","Manager"]).apply( lambda x: ",".join(x.Employee))
这将找到所有唯一的工作日期和经理集,并将员工与“,”放在一个字符串中
Group to aggregate,然后通过删除 Manager 并在适当的地方设置为 None 来修复 Employees。由于员工是独一无二的,因此集合在这里可以很好地移除经理。
s = df.groupby(['Job', 'Date']).agg({'Manager': 'first', 'Employee': lambda x: set(x)})
s['Employee'] = [', '.join(x.difference({y})) for x,y in zip(s.Employee, s.Manager)]
s['Employee'] = s.Employee.replace({'': None})
Manager Employee
Job Date
Job 1 2019-06-09 Tom Jim, Bill
2019-06-10 None Bill
Job 2 2019-06-10 Tom None
这里棘手的部分是从员工列中删除经理。
u = df.melt(['Job', 'Date'])
f = u[~u.duplicated(['Job', 'Date', 'value'], keep='last')].astype(str)
f.pivot_table(
index=['Job', 'Date'],
columns='variable', values='value',
aggfunc=','.join
).rename_axis(None, axis=1)
Employee Manager
Job Date
Job 1 2019-06-09 Jim,Bill Tom
2019-06-10 Bill None
Job 2 2019-06-10 NaN Tom
我偏爱用所需结果构建字典并重建数据框。
d = {}
for t in df.itertuples():
d_ = d.setdefault((t.Job, t.Date), {})
d_['Manager'] = t.Manager
d_.setdefault('Employees', set()).add(t.Employee)
for k, v in d.items():
v['Employees'] -= {v['Manager']}
v['Employees'] = ', '.join(v['Employees'])
pd.DataFrame(d.values(), d).rename_axis(['Job', 'Date']).reset_index()
Job Date Employees Manager
0 Job 1 2019-06-09 Bill, Jim Tom
1 Job 1 2019-06-10 Bill None
2 Job 2 2019-06-10 Tom
在你的情况下,尽量不要使用 lambda transform
+ drop_duplicates
df['Employee']=df['Employee'].mask(df['Employee'].eq(df.Manager)).dropna().groupby([df['Job'], df['Date']]).transform('unique').str.join(',')
df=df.drop_duplicates(['Job','Date'])
df
Out[745]:
Job Date Employee Manager
0 Job 1 2019-06-09 Jim,Bill Tom
3 Job 1 2019-06-10 Bill None
4 Job 2 2019-06-10 NaN Tom
假设我有以下数据集,变成了一个数据框:
data = [
['Job 1', datetime.date(2019, 6, 9), 'Jim', 'Tom'],
['Job 1', datetime.date(2019, 6, 9), 'Bill', 'Tom'],
['Job 1', datetime.date(2019, 6, 9), 'Tom', 'Tom'],
['Job 1', datetime.date(2019, 6, 10), 'Bill', None],
['Job 2', datetime.date(2019,6,10), 'Tom', 'Tom']
]
df = pd.DataFrame(data, columns=['Job', 'Date', 'Employee', 'Manager'])
这会产生一个如下所示的数据框:
Job Date Employee Manager
0 Job 1 2019-06-09 Jim Tom
1 Job 1 2019-06-09 Bill Tom
2 Job 1 2019-06-09 Tom Tom
3 Job 1 2019-06-10 Bill None
4 Job 2 2019-06-10 Tom Tom
我要生成的是每个独特 Job/Date 组合的一个枢轴,其中有一列用于经理,一列用于字符串,其中以逗号分隔,非经理员工。假设有几件事:
- 所有员工姓名都是唯一的(实际上我将使用唯一的员工 ID 而不是姓名),经理也是 "employees",因此永远不会出现员工和经理共享姓名的情况相同 name/id,但不同的个体。
- 一个工作组可以有经理,也可以没有(参见 id 为 3 的行,例如没有)
- 经理也将始终被列为员工(参见 ID 为 2 或 4 的行)
- 一个工作可以有一个经理,没有额外的员工(见第 id 4 行)
我希望生成的数据框看起来像:
Job Date Manager Employees
0 Job 1 2019-06-09 Tom Jim, Bill
1 Job 1 2019-06-10 None Bill
2 Job 2 2019-06-10 Tom None
这引出了我的问题:
- 有没有办法在 pandas 主元中执行类似聚合的 ','.join?
- 有没有办法使此聚合成为条件(排除管理器列中的 name/id)
我怀疑 1) 是可能的,2) 可能更难。如果 2) 是否定的,我可以稍后在我的代码中以其他方式绕过它。
怎么样
df.groupby(["Job","Date","Manager"]).apply( lambda x: ",".join(x.Employee))
这将找到所有唯一的工作日期和经理集,并将员工与“,”放在一个字符串中
Group to aggregate,然后通过删除 Manager 并在适当的地方设置为 None 来修复 Employees。由于员工是独一无二的,因此集合在这里可以很好地移除经理。
s = df.groupby(['Job', 'Date']).agg({'Manager': 'first', 'Employee': lambda x: set(x)})
s['Employee'] = [', '.join(x.difference({y})) for x,y in zip(s.Employee, s.Manager)]
s['Employee'] = s.Employee.replace({'': None})
Manager Employee
Job Date
Job 1 2019-06-09 Tom Jim, Bill
2019-06-10 None Bill
Job 2 2019-06-10 Tom None
这里棘手的部分是从员工列中删除经理。
u = df.melt(['Job', 'Date'])
f = u[~u.duplicated(['Job', 'Date', 'value'], keep='last')].astype(str)
f.pivot_table(
index=['Job', 'Date'],
columns='variable', values='value',
aggfunc=','.join
).rename_axis(None, axis=1)
Employee Manager
Job Date
Job 1 2019-06-09 Jim,Bill Tom
2019-06-10 Bill None
Job 2 2019-06-10 NaN Tom
我偏爱用所需结果构建字典并重建数据框。
d = {}
for t in df.itertuples():
d_ = d.setdefault((t.Job, t.Date), {})
d_['Manager'] = t.Manager
d_.setdefault('Employees', set()).add(t.Employee)
for k, v in d.items():
v['Employees'] -= {v['Manager']}
v['Employees'] = ', '.join(v['Employees'])
pd.DataFrame(d.values(), d).rename_axis(['Job', 'Date']).reset_index()
Job Date Employees Manager
0 Job 1 2019-06-09 Bill, Jim Tom
1 Job 1 2019-06-10 Bill None
2 Job 2 2019-06-10 Tom
在你的情况下,尽量不要使用 lambda transform
+ drop_duplicates
df['Employee']=df['Employee'].mask(df['Employee'].eq(df.Manager)).dropna().groupby([df['Job'], df['Date']]).transform('unique').str.join(',')
df=df.drop_duplicates(['Job','Date'])
df
Out[745]:
Job Date Employee Manager
0 Job 1 2019-06-09 Jim,Bill Tom
3 Job 1 2019-06-10 Bill None
4 Job 2 2019-06-10 NaN Tom