Как переназначить фрейм данных с помощью dict, содержащего вложенный список, с помощью функции map?

#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, это хорошо, я просто хотел указать на оговорку при использовании этого.