Слияние фрейма данных Pandas с подсчетами частоты

#python-3.x #pandas #dataframe

#python-3.x #pandas #фрейм данных

Вопрос:

У меня есть фрейм данных (df1) с такими сведениями о студенте, как —

 Student ID     Course Code       Mark
   1              C001            88  
   1              C002            71
   2              C003            67
   3              C002            92
   3              C001            66
   3              C004            70
   4              C004            65
  

и другой фрейм данных (df2) , который имеет

 WR ID        K ID        Course Code
SP-RS-01     K001        C002, C004
SP-RS-01     K004        C002
SP-RS-02     K005
SP-RS-03     K004        C003, C004
SP-RS-03     K006        C001
  

Теперь мне нужен фрейм данных, который включает идентификатор KID и WR для каждого идентификатора студента в соответствии с пройденными ими курсами. И, возможно, укажите количество (как словарь), если они делали это более одного раза. Итак, что-то вроде этого, может быть —

 Student ID       Courses           KID              WR ID
  1             C001, C002        K006, K001, K004  SP-RS-03
  2             C003              K004              SP-RS-01, SP-RS-03
  3             C001, C002, C004  K001x2, K006      SP-RS-01, SP-RS-03, 
                                  K004x2
  4             C004              K004              SP-RS-01, SP-RS-03
  

Как мне это сделать?

Комментарии:

1. Один вопрос: должен ли идентификатор студента 3 k быть K001x2, K006, K004x2?

2.Я не слежу, у Student ID 1 есть курсы C001, C002 , почему это KID просто K006 ?

3. Как уже упоминалось, ваш ожидаемый результат не имеет смысла. Пожалуйста, исправьте или объясните подробнее.

4. Отредактировано. Извините за путаницу.

Ответ №1:

Вы можете использовать:

 #first flatten values pslitted by ,
s = (df2.set_index(['WR ID','K ID'])['Course Code']
        .str.split(',s ', expand=True)
        .stack()
        .reset_index(level=2, drop=True)
        .rename('Course Code')
        )
#print (s)

#aggregate list per Course Code
df2 = (df2.drop('Course Code', axis=1)
          .join(s, on=['WR ID','K ID'])
          .groupby('Course Code')
          .agg(list)
          .reset_index()
          )

print (df2)
  Course Code                 WR ID          K ID
0        C001            [SP-RS-03]        [K006]
1        C002  [SP-RS-01, SP-RS-01]  [K001, K004]
2        C003            [SP-RS-03]        [K004]
3        C004  [SP-RS-01, SP-RS-03]  [K001, K004]
  

 from collections import Counter

#combination flattening nested lists, Counter and new format with counts
f = lambda x: ', '.join(f'{k}x{v}' if v > 1 else k 
                        for k, v in Counter([z for y in x for z in y]).items())
#merge together and aggregate again
df = (df1.merge(df2, on='Course Code', how='left')
         .groupby('Student ID')
         .agg({'Course Code':', '.join,
               'WR ID':f,
               'K ID':f})
         .reset_index()
      )
print (df)
   Student ID       Course Code                   WR ID                  K ID
0           1        C001, C002    SP-RS-03, SP-RS-01x2      K006, K001, K004
1           2              C003                SP-RS-03                  K004
2           3  C002, C001, C004  SP-RS-01x3, SP-RS-03x2  K001x2, K004x2, K006
3           4              C004      SP-RS-01, SP-RS-03            K001, K004
  

Редактировать:

Проблема в некоторых отсутствующих значениях, решение — преобразовать их в пустые списки:

 from collections import Counter

#combination flattening nested lists, Counter and new format with counts
f = lambda x: ', '.join(f'{k}x{v}' if v > 1 else k 
                        for k, v in Counter([z for y in x for z in y]).items())

#merge together and aggregate again
df = df1.merge(df2, on='Course Code', how='left')
df[['WR ID','K ID']] = df[['WR ID','K ID']].applymap(lambda x: x if x==x else [])

df = (df.groupby('Student ID')
        .agg({'Course Code':', '.join,
               'WR ID':f,
               'K ID':f})
         .reset_index()
      )
  

Комментарии:

1. Привет @jezrael. Это выдает мне 'float' object not iterable ошибку в f = lambda x ... строке.

2. @harry04 — Отредактированный ответ. Пожалуйста, проверьте это.

3. Сейчас это не выдает мне никаких ошибок, но результат сильно отличается от вашего. Я получаю только WR ID и K ID для 4-й строки Student ID . Все остальные пустые.

4. Первая часть дает мне результирующий результат df , где некоторые строки для K ID похожи [nan, nan, K016, nan, K068] , потому что у меня нет для них никаких значений. Во второй части приводится, df где я получаю только K ID и WR ID значения для 4-й строки.

5. Конечно, я могу отправить вам это!