Группировка по пандам датафрейм и объединение строк с запятой на основе частоты значений в столбце - PullRequest
0 голосов
/ 09 мая 2018

Это обновление структуры моего DataFrame, я сформулировал структуру в спешке, я проверял одного пользователя и смоделировал эту структуру. Замечание @ liliscent: «данные случайно удовлетворяют этому условию» также верно, и value_counts и cum_sum () решают его. Но затем также изменяются user_id, и разные пользователи могут иметь один и тот же meet_id, если у них одинаковый текст.

Обновлена ​​структура DataFrames:

   mytable = pd.DataFrame({'user_id': [ '3c', '3c', '3c', '3c','3c', '3c', '3c', '3c', '3c', '3c', '3c', '3c', '3c', '3d',
                                 '3d', '3d', '3d', '3e', '3e', '3r', '3w', '3w', '3w', '3w'],
              'meet_id': [1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,5,6,1,2,1,1], 'text': ['abc', 'abc', 'abc', 'abc', 'abc', 'abc', 'abc',
        'xyz', 'xyz', 'xyz', 'xyz', 'xyz', 'xyz', 'npq', 'npq', 'npq', 'npq', 'tt', 'op', 'li', 'abc', 'xyz', 'abc', 'abc'], 'label': ['A', 'A', 'A', 'A', 'A','B', 'B', 'B', 'B', 'B',
    'C', 'C', 'A', 'G', 'H', 'H', 'H', 'A', 'A', 'B', 'E', 'G', 'B', 'B']})
   mytable =  mytable[['user_id', 'meet_id', 'text', 'label']] # ordering columns in the way I would like to be printed out.

   user_id  meet_id  text label
   3c        1      abc     A
   3c        1      abc     A
   3c        1      abc     A
   3c        1      abc     A
   3c        1      abc     A
   3c        1      abc     B
   3c        1      abc     B
   3c        2      xyz     B
   3c        2      xyz     B
   3c        2      xyz     B
   3c        2      xyz     C
   3c        2      xyz     C
   3c        2      xyz     A
   3d        3      npq     G
   3d        3      npq     H
   3d        3      npq     H
   3d        3      npq     H
   3e        4      tt      A
   3e        5      op      A
   3r        6      li      B
   3w        1      abc     E
   3w        2      xyz     G 
   3w        1      abc     B
   3w        1      abc     B

Я бы хотел сгруппировать по столбцу [user_id & meet_id] и объединить столбец метки таким образом, чтобы метка с более высокой частотой для этой группы оставалась нетронутой, в то время как вторая наиболее часто встречающаяся метка будет объединять первую метку, и последний ярлык объединит все ярлыки.

обновленный вывод DataFrame - это то, что я ищу

    mytable_pro = pd.DataFrame({'user_id': ['3c', '3c', '3c', '3c','3c', '3c', '3c', '3c', '3c', '3c', '3c', '3c', '3c','3d',
                                 '3d', '3d', '3d', '3e', '3e', '3r', '3w', '3w', '3w', '3w'],
              'meet_id': [1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,5,6,1,2,1,1], 'text': ['abc', 'abc', 'abc', 'abc', 'abc', 'abc', 'abc',
        'xyz', 'xyz', 'xyz', 'xyz', 'xyz', 'xyz','npq', 'npq', 'npq', 'npq', 'tt', 'op', 'li', 'abc', 'xyz', 'abc', 'abc' ], 'label': ['A', 'A', 'A', 'A', 'A', 'B,A', 'B,A', 'B', 'B', 'B',
    'B, C', 'B, C', 'A,B,C', 'H,G', 'H', 'H', 'H', 'A', 'A', 'B', 'E,B', 'G', 'B', 'B']})
    mytable_pro = mytable_pro[['user_id', 'meet_id', 'text', 'label']] # ordering columns in the way I would like to be printed out.

Это дает:

    user_id  meet_id text  label
   3c        1       abc      A
   3c        1       abc      A
   3c        1       abc      A
   3c        1       abc      A
   3c        1       abc      A
   3c        1       abc     B,A
   3c        1       abc     B,A
   3c        2       xyz      B
   3c        2       xyz      B
   3c        2       xyz      B
   3c        2       xyz    B, C
   3c        2       xyz    B, C
   3c        2       xyz    A,B,C
   3d        3       npq     H,G
   3d        3       npq      H
   3d        3       npq      H
   3d        3       npq      H
   3e        4       tt       A
   3e        5       op       A
   3r        6       li       B
   3w        1       abc     E,B
   3w        2       xyz      G
   3w        1       abc      B
   3w        1       abc      B

Ответ, данный @piRSquared:

    mytable.groupby('meet_id').label.value_counts().groupby('meet_id').apply(
lambda d: d.index.to_series().str[1].cumsum().str.join(', '))        

- это ПРАВИЛЬНЫЙ ОТВЕТ на НЕПРАВИЛЬНЫЙ вопрос, который я задал, большое спасибо и очень жаль. Это решает проблему заказа, как упомянуто ранее, но не будет работать, если другой пользователь имеет тот же meet_id. Просто чтобы быть исчерпывающим, если частота метки оказывается одинаковой для группы, не имеет значения, какая из меток получает другую объединенную.

Даёт:

     user_id  meet_id  text       label
   3c          1         abc           A
   3c          1         abc           A
   3c          1         abc           A
   3c          1         abc           A
   3c          1         abc           A
   3c          1         abc        A, B
   3c          1         abc        A, B
   3c          2         xyz           B
   3c          2         xyz           B
   3c          2         xyz           B
   3c          2         xyz        B, C
   3c          2         xyz        B, C
   3c          2         xyz     B, C, A
   3d          3         npq        H, G
   3d          3         npq           H
   3d          3         npq           H
   3d          3         npq           H
   3e          4          tt           A
   3e          5          op           A
   3r          6          li           B
   3w          1         abc     A, B, E
   3w          2         xyz    B, C, A, G
   3w          1         abc        A, B
   3w          1         abc        A, B

Метки для 3w отключены, так как метки для meet_id подобраны, игнорируя разницу user_id. Виноват!

Теперь, поскольку user_id также должен учитываться, я попробовал следующее:

    s = mytable.groupby(['user_id', 'meet_id']).label.value_counts().groupby(['user_id, 'meet_id']).apply(
lambda d: d.index.to_series().str[1].cumsum().str.join(', '))        

Это броски:

    AttributeError: Can only use .str accessor with string values, which use np.object_ dtype in pandas

Ах! Еще одно небольшое обновление: на самом деле у меня есть слова в колонке с ярлыком.

    dummy_boo = pd.DataFrame({'user_id': ['3g', '3g', '3g'], 'meet_id': [9,9,9], 'text': ['baby', 'baby', 'baby'], 'label':['hello', 'hello', 'why']}

Выход:

    user_id  meet_id  text  label
      3g        9     baby  hello
      3g        9     baby  hello
      3g        9     baby   why

Применение приведенного выше кода приводит к тому, что каждый символ разделяется запятой.

 user_id  meet_id  text   label
  3g        9      baby  h, e, l, l, o
  3g        9      baby  h, e, l, l, o
  3g        9      baby  h, e, l, l, o, w, h, y

Вместо этого мне нужно:

    user_id  meet_id  text   label
  3g        9      baby    hello
  3g        9      baby    hello
  3g        9      baby    hello, why

Тип d для метки - это объект. Должны ли мы использовать вместо этого Astype. Большое спасибо всем за помощь.

Ответы [ 3 ]

0 голосов
/ 09 мая 2018

Вы можете попробовать что-то вроде следующего:

mytable['label'] = (mytable.groupby('meet_id')
                    .label.transform(lambda x: list(x.cumsum()))
                    .apply(set))

>>> mytable
   user_id  meet_id text      label
0       3c        1  abc        {A}
1       3c        1  abc        {A}
2       3c        1  abc        {A}
3       3c        1  abc        {A}
4       3c        1  abc        {A}
5       3c        1  abc     {A, B}
6       3c        1  abc     {A, B}
7       3c        2  xyz        {B}
8       3c        2  xyz        {B}
9       3c        2  xyz        {B}
10      3c        2  xyz     {C, B}
11      3c        2  xyz     {C, B}
12      3c        2  xyz  {C, B, A}

Если вы хотите избавиться от типа данных set и просто указать его как строку (как в желаемом выводе), вы можете применить ', '.join(sorted(set(x)))) вместо простого set (спасибо @Wen и @ScottBoston ):

mytable['label'] = (mytable.groupby('meet_id')
                    .label.transform(lambda x: list(x.cumsum()))
                    .apply(lambda x: ', '.join(sorted(set(x)))))
>>> mytable
   user_id  meet_id text    label
0       3c        1  abc        A
1       3c        1  abc        A
2       3c        1  abc        A
3       3c        1  abc        A
4       3c        1  abc        A
5       3c        1  abc     A, B
6       3c        1  abc     A, B
7       3c        2  xyz        B
8       3c        2  xyz        B
9       3c        2  xyz        B
10      3c        2  xyz     B, C
11      3c        2  xyz     B, C
12      3c        2  xyz  A, B, C
0 голосов
/ 09 мая 2018

Редактировать: Хорошо, намного более простое решение:

mytable['label'] = mytable.groupby(['user_id','meet_id','text'])['label']\
       .apply(lambda x: x.cumsum()).apply(lambda x: sorted(set(x)))

Моя безобразная попытка:

mytable['label'] = mytable.groupby(['user_id','meet_id','text'])['label']\
      .apply(lambda x: x.cumsum().str.extractall('(.)')\
                        .groupby(level=0)[0].apply(lambda x: sorted(set(x))))

Выход:

   user_id  meet_id text      label
0       3c        1  abc        [A]
1       3c        1  abc        [A]
2       3c        1  abc        [A]
3       3c        1  abc        [A]
4       3c        1  abc        [A]
5       3c        1  abc     [A, B]
6       3c        1  abc     [A, B]
7       3c        2  xyz        [B]
8       3c        2  xyz        [B]
9       3c        2  xyz        [B]
10      3c        2  xyz     [B, C]
11      3c        2  xyz     [B, C]
12      3c        2  xyz  [A, B, C]
0 голосов
/ 09 мая 2018

value_counts и cumsum

value_counts сортировка по убыванию

cols = ['meet_id', 'user_id']
s = mytable.groupby(cols).label.value_counts().groupby(cols).apply(
    lambda d: d.index.to_series().str[-1].cumsum().str.join(', ')
)

mytable.assign(label=[s.get((a, b, c)) for a, b, c in mytable[cols + ['label']].values])

   user_id  meet_id text    label
0       3c        1  abc        A
1       3c        1  abc        A
2       3c        1  abc        A
3       3c        1  abc        A
4       3c        1  abc        A
5       3c        1  abc     A, B
6       3c        1  abc     A, B
7       3c        2  xyz        B
8       3c        2  xyz        B
9       3c        2  xyz        B
10      3c        2  xyz     B, C
11      3c        2  xyz     B, C
12      3c        2  xyz  B, C, A
13      3d        3  npq     H, G
14      3d        3  npq        H
15      3d        3  npq        H
16      3d        3  npq        H
17      3e        4   tt        A
18      3e        5   op        A
19      3r        6   li        B
20      3w        1  abc     B, E
21      3w        2  xyz        G
22      3w        1  abc        B
23      3w        1  abc        B

Включите sorted, а также

cols = ['meet_id', 'user_id']
s = mytable.groupby(cols).label.value_counts().groupby(cols).apply(
    lambda d: d.index.to_series().str[-1].cumsum().apply(sorted).str.join(', ')
)

mytable.assign(label=[s.get((a, b, c)) for a, b, c in mytable[cols + ['label']].values])

   user_id  meet_id text    label
0       3c        1  abc        A
1       3c        1  abc        A
2       3c        1  abc        A
3       3c        1  abc        A
4       3c        1  abc        A
5       3c        1  abc     A, B
6       3c        1  abc     A, B
7       3c        2  xyz        B
8       3c        2  xyz        B
9       3c        2  xyz        B
10      3c        2  xyz     B, C
11      3c        2  xyz     B, C
12      3c        2  xyz  A, B, C
13      3d        3  npq     G, H
14      3d        3  npq        H
15      3d        3  npq        H
16      3d        3  npq        H
17      3e        4   tt        A
18      3e        5   op        A
19      3r        6   li        B
20      3w        1  abc     B, E
21      3w        2  xyz        G
22      3w        1  abc        B
23      3w        1  abc        B

И для поправки на слова, а не на отдельные символы

cols = ['meet_id', 'user_id']
s = mytable.groupby(cols).label.value_counts().groupby(cols).apply(
    lambda d: d.index.to_series().str[-1].add('|').cumsum().apply(
        lambda e: ', '.join(sorted(e.strip('|').split('|')))
    )
)

mytable.assign(label=[s.get((a, b, c)) for a, b, c in mytable[cols + ['label']].values])

Старый ответ

С transform и пользовательской кумулятивной уникальной функцией

from collections import Counter

def cum_unique(x):
    return pd.Series(list(map(
        Counter, x
    ))).cumsum().str.join(', ')

mytable.assign(label=mytable.groupby('meet_id').label.transform(cum_unique))

   user_id  meet_id text    label
0       3c        1  abc        A
1       3c        1  abc        A
2       3c        1  abc        A
3       3c        1  abc        A
4       3c        1  abc        A
5       3c        1  abc     A, B
6       3c        1  abc     A, B
7       3c        2  xyz        B
8       3c        2  xyz        B
9       3c        2  xyz        B
10      3c        2  xyz     B, C
11      3c        2  xyz     B, C
12      3c        2  xyz  B, C, A

укороченная версия

mytable.assign(label=mytable.groupby('meet_id').label.transform(
    lambda x: pd.Series(list(map(Counter, x))).cumsum().str.join(', ')
))

За комментарий

по сиреневый

Сначала можно отсортировать по meet_id и размеру группы

sizes = mytable.groupby(['meet_id', 'label']).label.transform('size')

m1 = mytable.assign(sizes=sizes).sort_values(
    ['meet_id', 'sizes'], ascending=[True, False]).drop('sizes', 1)
m1

m1.assign(label=m1.groupby('meet_id').label.transform(
    lambda x: pd.Series(list(map(Counter, x))).cumsum().str.join(', ')
)).reindex(mytable.index)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...