Pandas - поворот столбца в (условную) агрегированную строку - PullRequest
4 голосов
/ 24 июня 2019

Допустим, у меня есть следующий набор данных, превращенный в фрейм данных:

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

То, что я пытаюсь сгенерировать, этосводка для каждого уникального комбинированного задания / даты с столбцом для менеджера и столбцом для строки с разделенными запятыми сотрудниками, не являющимися менеджерами.Можно предположить пару вещей:

  1. Все имена сотрудников уникальны (на самом деле я буду использовать уникальные идентификаторы сотрудников, а не имена), а менеджеры также являются «сотрудниками», поэтому никогда не будетслучай, когда сотрудник и менеджер имеют одно и то же имя / идентификатор, но являются разными лицами.
  2. У рабочей бригады может быть менеджер или нет (см. строку с идентификатором 3, например, без)
  3. Менеджер также всегда будет указан как сотрудник (см. Строку с идентификатором 2 или 4)
  4. В задании может быть менеджер без дополнительных сотрудников (см. Идентификатор строки 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

Это приводит к моим вопросам:

  1. Есть ли способ сделать ','.присоединиться, как агрегация в своде панд?
  2. Есть ли способ сделать эту агрегацию условной (исключая имя / идентификатор в столбце менеджера)

Я подозреваю 1) возможно,и 2) может быть более сложным.Если 2) - нет, я могу обойти это другими способами позже в моем коде.

Ответы [ 5 ]

4 голосов
/ 24 июня 2019

Сложная часть - удаление менеджера из столбца Сотрудник.


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
3 голосов
/ 24 июня 2019

Я неравнодушен к созданию словаря с желаемыми результатами и реконструкции фрейма данных.

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
3 голосов
/ 24 июня 2019

Сгруппируйте, чтобы объединить, затем исправьте сотрудников, удалив Manager и установив None, где это необходимо. Поскольку сотрудники уникальны, наборы будут хорошо работать здесь, чтобы удалить менеджера.

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
2 голосов
/ 24 июня 2019

В вашем случае попробуйте не использовать лямбда 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
0 голосов
/ 24 июня 2019

как насчет

df.groupby(["Job","Date","Manager"]).apply( lambda x: ",".join(x.Employee))

, при этом будут найдены все уникальные наборы Даты работы и Менеджера и собраны сотрудники вместе с "," в одну строку

...