Как объединить строку с разделенными запятыми элементами столбца в список с помощью Pandas groupby ()? - PullRequest
4 голосов
/ 07 ноября 2019

У меня есть данные, подобные следующим:

NAME    ETHNICITY_RECAT TOTAL_LENGTH    3LETTER_SUBSTRINGS
joseph  fr              14              jos, ose, sep, eph
ann     en              16              ann
anne    ir              14              ann, nne
tom     en              18              tom
tommy   fr              16              tom, omm, mmy
ann     ir              19              ann
... more rows

Значения 3LETTER_SUBSTRINGS - это строка, которая захватывает все трехбуквенные подстроки переменной NAME. Я хотел бы объединить его в один список, в котором каждый разделенный запятыми элемент добавляется в список каждой строкой и должен рассматриваться как один элемент списка. Следующее:

ETHNICITY_RECAT TOTAL_LENGTH            3LETTER_SUBSTRINGS
                min max mean            <lambda>
fr              2   26  13.22           [jos, ose, sep, eph, tom, oom, mmy, ...]
en              3   24  11.92           [ann, tom, ...]
ir              4   23  12.03           [ann, nne, ann, ...]

Я вроде "сделал" это с помощью следующего кода:

aggregations = {
    'TOTAL_LENGTH': [min, max, 'mean'], 
    '3LETTER_SUBSTRINGS': lambda x: list(x),
    }

self.df_agg = self.df.groupby('ETHNICITY_RECAT', as_index=False).agg(aggregations)

Проблема в том, что вся строка "ann, anne" считается одним элементом спискав последнем списке, вместо того, чтобы рассматривать каждый элемент как отдельный список, такой как «ann», «anne».

Я хотел бы видеть самую высокую частоту подстрок, но теперь я получаю частотувсю строку (вместо отдельной трехбуквенной подстроки), когда я запускаю следующий код:

from collections import Counter 
x = self.df_agg_eth[self.df_agg_eth['ETHNICITY_RECAT']=='en']['3LETTER_SUBSTRINGS']['<lambda>']
x_list = x[0]
c = Counter(x_list)

Я получаю это:

[('jos, ose, sep, eph', 19), ('ann, nee', 5), ...]

Вместо того, что я хочу:

[('jos', 19), ('ose', 19), ('sep', 23), ('eph', 19), ('ann', 15), ('nee', 5), ...]

Я пытался:

'3LETTER_SUBSTRINGS': lambda x: list(i) for i in x.split(', '),

Но там написано invalid syntax.

Ответы [ 2 ]

2 голосов
/ 07 ноября 2019

Первое, что вы хотите сделать, это преобразовать строку в список, затем это просто groupby с agg:

df['3LETTER_SUBSTRINGS'] = df['3LETTER_SUBSTRINGS'].str.split(', ')

df.groupby('ETHNICITY_RECAT').agg({'TOTAL_LENGTH':['min','max','mean'],
                                   '3LETTER_SUBSTRINGS':'sum'})

Вывод:

                TOTAL_LENGTH                             3LETTER_SUBSTRINGS
                         min max  mean                                  sum
ETHNICITY_RECAT                                                            
en                        16  18  17.0                           [ann, tom]
fr                        14  16  15.0  [jos, ose, sep, eph, tom, omm, mmy]
ir                        14  19  16.5                      [ann, nne, ann]
1 голос
/ 07 ноября 2019

Я думаю, что большая часть вашего кода в порядке, вы просто неверно истолковали ошибку: она не имеет ничего общего с преобразованием строк. У вас есть списки / кортежи в каждой ячейке столбца 3LETTER_SUBSTRING. Когда вы используете функцию lambda x:list(x), вы создаете список кортежей. Следовательно, нет ничего похожего на split(","), который нужно выполнить и выполнить приведение к строке и обратно к таблице ...

Вместо этого вам просто нужно развернуть таблицу при создании нового списка. Итак, вот небольшой воспроизводимый код: (обратите внимание, что я сосредоточился на вашей проблеме с кортежем / агрегацией, так как уверен, что вы быстро найдете остальную часть кода)

import pandas as pd
# Create some data
names = [("joseph","fr"),("ann","en"),("anne","ir"),("tom","en"),("tommy","fr"),("ann","fr")]
df = pd.DataFrame(names, columns=["NAMES","ethnicity"])
df["3LETTER_SUBSTRING"] = df["NAMES"].apply(lambda name: [name[i:i+3] for i in range(len(name) - 2)])
print(df)
# Aggregate the 3LETTER per ethnicity, and unnest the result in a new table for each ethnicity:
df.groupby('ethnicity').agg({
    "3LETTER_SUBSTRING": lambda x:[z for y in x for z in y]
})

Используя указанный вами счетчик, я получил

dfg = df.groupby('ethnicity', as_index=False).agg({
    "3LETTER_SUBSTRING": lambda x:[z for y in x for z in y]
})
from collections import Counter
print(Counter(dfg[dfg["ethnicity"] == "en"]["3LETTER_SUBSTRING"][0]))
# Counter({'ann': 1, 'tom': 1})

Чтобы получить его в виде списка кортежей, просто используйте встроенную функцию словаря, такую ​​как dict.items().


ОБНОВЛЕНИЕ : использованиесписок предварительно отформатированных строк, как в вопросе:

import pandas as pd
# Create some data
names = [("joseph","fr","jos, ose, sep, eph"),("ann","en","ann"),("anne","ir","ann, nne"),("tom","en","tom"),("tommy","fr","tom, omm, mmy"),("ann","fr","ann")]
df = pd.DataFrame(names, columns=["NAMES","ethnicity","3LETTER_SUBSTRING"])
def transform_3_letter_to_table(x):
    """
    Update this function with regard to your data format
    """
    return x.split(", ")
df["3LETTER_SUBSTRING"] = df["3LETTER_SUBSTRING"].apply(transform_3_letter_to_table)
print(df)
# Applying aggregation
dfg = df.groupby('ethnicity', as_index=False).agg({
    "3LETTER_SUBSTRING": lambda x:[z for y in x for z in y]
})
print(dfg)
# test on some data
from collections import Counter
c = Counter(dfg[dfg["ethnicity"] == "en"]["3LETTER_SUBSTRING"][0])
print(c)
print(list(c.items()))
...