2016-03-28 3 views
6

У меня есть pandas dataframe с несколькими строками, которые находятся рядом с дубликатами друг друга, за исключением одного значения. Моя цель - объединить или «объединить» эти строки в одну строку, не суммируя числовые значения.pandas - Объединить почти повторяющиеся строки на основе значения столбца

Вот пример того, что я работаю с:

Name Sid Use_Case Revenue 
A  xx01 Voice  $10.00 
A  xx01 SMS  $10.00 
B  xx02 Voice  $5.00 
C  xx03 Voice  $15.00 
C  xx03 SMS  $15.00 
C  xx03 Video  $15.00 

А вот то, что я хотел бы:

Name Sid Use_Case   Revenue 
A  xx01 Voice, SMS   $10.00 
B  xx02 Voice    $5.00 
C  xx03 Voice, SMS, Video $15.00 

причина, почему я не хочу, чтобы подвести «Прибыльность столбец потому, что моя таблица является результатом поворота в течение нескольких периодов времени, когда «Доход» просто заканчивается тем, что перечисляется несколько раз вместо того, чтобы иметь другое значение для «Use_Case».

Что было бы лучшим способом решить эту проблему? Я просмотрел функцию groupby(), но я до сих пор не очень хорошо ее понимаю.

+0

Если мой ответ или ответ Ами был полезен, не забудьте [принять] (http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work) его. Благодарю. – jezrael

+0

Спасибо! Они оба работают хорошо. Мне может потребоваться объединить несколько столбцов, так как мой фактический набор данных намного шире, чем я предоставил 4, но это должно сделать трюк. –

ответ

5

Я думаю, вы можете использовать groupby с aggregatefirst и пользовательские функции ', '.join:

df = df.groupby('Name').agg({'Sid':'first', 
          'Use_Case': ', '.join, 
          'Revenue':'first' }).reset_index() 

#change column order       
print df[['Name','Sid','Use_Case','Revenue']]        
    Name Sid   Use_Case Revenue 
0 A xx01   Voice, SMS $10.00 
1 B xx02    Voice $5.00 
2 C xx03 Voice, SMS, Video $15.00 

Хорошая идея от комментариев, спасибо Goyo:

df = df.groupby(['Name','Sid','Revenue'])['Use_Case'].apply(', '.join).reset_index() 

#change column order       
print df[['Name','Sid','Use_Case','Revenue']]        
    Name Sid   Use_Case Revenue 
0 A xx01   Voice, SMS $10.00 
1 B xx02    Voice $5.00 
2 C xx03 Voice, SMS, Video $15.00 
+1

Я бы на всякий случай сгруппировал все, кроме «Use_Case». Кроме того, агрегированная функция может быть просто '', '.join', не нужно использовать 'lambda'. – Goyo

+0

Выключает это, если ваш столбец имеет значения, которые' join' не нравится. Я должен был выбросить '.map (str)' перед тем, как «применить», чтобы он работал чисто. –

+1

Да или используйте '.astype (str)', это функция для строки в строку. – jezrael

1

Вы можете groupby и apply функция list:

>>> df['Use_Case'].groupby([df.Name, df.Sid, df.Revenue]).apply(list).reset_index() 
    Name Sid  Revenue  0 
0 A xx01 $10.00 [Voice, SMS] 
1 B xx02 $5.00 [Voice] 
2 C xx03 $15.00 [Voice, SMS, Video] 

(В случае, если вы беспокоитесь о дубликатах, используйте set вместо list.)

1

я использовал код, который я не думаю, было оптимальным и в конце концов нашел jezrael's answer. Но после того, как использовать его и запустить тест timeit, я на самом деле вернулся к тому, что я делал, что было:

cmnts = {} 
for i, row in df.iterrows(): 
    while True: 
     try: 
      if row['Use_Case']: 
       cmnts[row['Name']].append(row['Use_Case']) 

      else: 
       cmnts[row['Name']].append('n/a') 

      break 

     except KeyError: 
      cmnts[row['Name']] = [] 

df.drop_duplicates('Name', inplace=True) 
df['Use_Case'] = ['; '.join(v) for v in cmnts.values()] 

Согласно моему испытанию 100 пробег timeit, то итерация и заменить метод на порядок быстрее, чем метод groupby.

import pandas as pd 
from my_stuff import time_something 

df = pd.DataFrame({'a': [i/(i % 4 + 1) for i in range(1, 10001)], 
        'b': [i for i in range(1, 10001)]}) 

runs = 100 

interim_dict = 'txt = {}\n' \ 
       'for i, row in df.iterrows():\n' \ 
       ' try:\n' \ 
       "  txt[row['a']].append(row['b'])\n\n" \ 
       ' except KeyError:\n' \ 
       "  txt[row['a']] = []\n" \ 
       "df.drop_duplicates('a', inplace=True)\n" \ 
       "df['b'] = ['; '.join(v) for v in txt.values()]" 

grouping = "new_df = df.groupby('a')['b'].apply(str).apply('; '.join).reset_index()" 

print(time_something(interim_dict, runs, beg_string='Interim Dict', glbls=globals())) 
print(time_something(grouping, runs, beg_string='Group By', glbls=globals())) 

выходы:

Interim Dict 
    Total: 59.1164s 
    Avg: 591163748.5887ns 

Group By 
    Total: 430.6203s 
    Avg: 4306203366.1827ns 

, где time_something является функцией, которая раз фрагмент с timeit и возвращает результат в указанном выше формате.

Смежные вопросы