#pandas #dataframe #series
Вопрос:
У меня есть такой df:
ID
0 123
1 123
2 123
3 123
4 123
5 123
6 456
7 456
8 456
9 123
Я хочу добавить новый столбец с map
функцией с помощью dict d:
d = {'123': [1, 2, 3, 1, 2, 1, 5], '456': [1, 2, 1]}
Ожидаемый результат:
ID Count
0 123 1
1 123 2
2 123 3
3 123 1
4 123 2
5 123 1
6 456 1
7 456 2
8 456 1
9 123 5
Но df.ID.map(d)
возвращается:
0 [1, 2, 3, 1, 2, 1, 5]
1 [1, 2, 3, 1, 2, 1, 5]
2 [1, 2, 3, 1, 2, 1, 5]
3 [1, 2, 3, 1, 2, 1, 5]
4 [1, 2, 3, 1, 2, 1, 5]
5 [1, 2, 3, 1, 2, 1, 5]
6 [1, 2, 1]
7 [1, 2, 1]
8 [1, 2, 1]
9 [1, 2, 3, 1, 2, 1, 5]
Заранее спасибо за вашу помощь!
Комментарии:
1. карта с dict может работать только с уникальным ключом , и у вашей целевой df есть несколько дубликатов
2. Спасибо, БЕНИ! Что я могу использовать вместо карты ?
Ответ №1:
Вы можете использовать groupby
apply
:
df.groupby('ID').apply(lambda g: pd.Series(d[g.name]))
Пример:
>>> df['Count'] = df.groupby('ID').apply(lambda g: pd.Series(d[g.name])).to_list()
>>> df
ID Count
0 123 1
1 123 2
2 123 3
3 123 1
4 123 2
5 123 1
6 456 1
7 456 2
8 456 1
Редактировать. вариант для неупорядоченного ввода:
(df.join(df.groupby('ID').apply(lambda g: pd.Series(d[g.name],
name='Count',
index=g.index))
.droplevel(0))
)
выход:
ID Count
0 123 1
1 123 2
2 123 3
3 123 1
4 123 2
5 123 1
6 456 1
7 456 2
8 456 1
9 123 5
Комментарии:
1. Спасибо, мозуэй! Я добавил строку с идентификатором 123 в конце данных и запустил код, но не получил ожидаемого результата. То есть, если данные неупорядочены, ваш код не работает.
2. Можете ли вы привести пример (ввод/вывод) для этого случая? (Отредактируйте вопрос)
3. @tako0707 обновленный ответ
4. Да, но мне пришлось изменить
'123'
(строку) на123
(int). Это зависит от типа, который у вас есть в ваших данных.5. Теперь это работает. Я этого не заметил. Спасибо за ваше время и помощь. Ты идеальна!
Ответ №2:
Создайте кумулятивный счетчик на df
-> здесь предполагается, что количество для каждого идентификатора должно быть таким же, как количество значений для каждого идентификатора в d :
df = df.assign(counter = df.groupby('ID').cumcount())
Создайте фрейм данных из d
, используя pd.concat:
# converted the keys to integers
# so that it matches the dtype of ID in df
frame = pd.concat([pd.Series(val) for _, val in d.items()],
keys = map(int, d))
frame.name = 'Count'
Запустите a pd.merge
, чтобы выровнять frame
по df
:
df.merge(frame,
left_on = ['ID', 'counter'],
right_index = True).drop(columns='counter')
ID Count
0 123 1
1 123 2
2 123 3
3 123 1
4 123 2
5 123 1
6 456 1
7 456 2
8 456 1
9 123 5
Ответ №3:
От вас требуют, чтобы вы могли получить то, что вам нужно, после explode
pd.Series(d).explode().reset_index()
Out[115]:
index 0
0 123 1
1 123 2
2 123 3
3 123 1
4 123 2
5 123 1
6 456 1
7 456 2
8 456 1
Комментарии:
1. Отличный, но это не работает, если оригинал
df
имеет другой порядок. Пример123 456 123 456
.2. @Ch3steR У меня был точно такой же комментарий ^^ , в любом случае, хороший ответ
3. @mozway, ты тоже молодец. Если ответ БЕНИ работает для OP, это хорошо, я просто хотел указать на оговорку при использовании этого.