Как группировать строки вместе на основе условий из списка? Панды

#pandas #database #group-by

Вопрос:

Я хочу иметь возможность группировать строки в одну, если они имеют совпадающие значения в определенных столбцах, однако я хочу, чтобы они группировались только в том случае, если значение находится в списке. Например,

 team_sports = ['football', 'basketball']

view of df

country    sport      age
USA       football    21
USA       football    28
USA       golf        20
USA       golf        44
China     football    30
China     basketball  22
China     basketball  41

wanted outcome
country    sport      age
USA       football    21,28
USA       golf        20
USA       golf        44
China     football    30
China     basketball  22,41

The attempt I made was,

team_sports = ['football', 'basketball']

for i in df['Sport']:
  if i in team_sports:
     group_df= df.groupby(['Country', 'Sport'])['Age'].apply(list).reset_index() 
 

Это займет целую вечность, база данных, которую я использую, содержит 100 000 строк.

Действительно ценю любую помощь, спасибо

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

1. Почему ты делаешь это снова и снова? Не group_df= df.groupby(['Country', 'Sport'])['Age'].apply(list).reset_index() дает вам ожидаемого результата с первого раза?

2. @HenryEcker Я не уверен, что вы имеете в виду, но я поставил «для», чтобы он не группировал ряды разных видов спорта, например, выше, гольф. Пожалуйста, поправьте меня, если я неправильно истолковал.

3. Я вижу, я пропустил ту часть, которая касается только группировки строк в списке. Имеет ли значение порядок? вам нужно, чтобы они были в том порядке, в котором они появляются, или порядок не имеет значения, пока присутствуют все результаты?

4. @HenryEcker Заказ не имеет значения, пока есть результат

Ответ №1:

Более простой подход состоит в том, чтобы разделить фрейм данных на основе тех строк, в которых sports находится столбец isin списка team_sports . groupby aggregate по отдельности, а затем concat снова вместе:

 team_sports = ['football', 'basketball']

m = df['sport'].isin(team_sports)
cols = ['country', 'sport']
group_df = pd.concat([
    # Group those that do match condition
    df[m].groupby(cols, as_index=False)['age'].agg(list),
    # Leave those that don't match condition as is
    df[~m]
], ignore_index=True).sort_values(cols)
 

* sort_values необязательно объединять страну и спорт вместе

group_df :

   country       sport       age
0   China  basketball  [22, 41]
1   China    football      [30]
2     USA    football  [21, 28]
3     USA        golf        20
4     USA        golf        44
 

Менее простым подходом было бы создать новый уровень группировки на основе того, есть ли значение в списке командных видов спорта с использованием isin cumsum :

 team_sports = ['football', 'basketball']

group_df = (
    df.groupby(
        ['country', 'sport',
         (~df['sport'].sort_values().isin(team_sports)).cumsum().sort_index()],
        as_index=False,
        sort=False
    )['age'].agg(list)
)
 

group_df :

   country       sport       age
0     USA    football  [21, 28]
1     USA        golf      [20]
2     USA        golf      [44]
3   China    football      [30]
4   China  basketball  [22, 41]
 

Как создаются группы:

 team_sports = ['football', 'basketball']

print(pd.DataFrame({
    'country': df['country'],
    'sport': df['sport'],
    'not_in_team_sports': (~df['sport'].sort_values()
                           .isin(team_sports)).cumsum().sort_index()
}))
 
   country       sport  not_in_team_sports
0     USA    football                   0
1     USA    football                   0
2     USA        golf                   1  # golf 1
3     USA        golf                   2  # golf 2 (not in the same group)
4   China    football                   0
5   China  basketball                   0
6   China  basketball                   0
 

* sort_values необходимо здесь, чтобы sport группы не прерывались видами спорта, которых нет в списке.

 df = pd.DataFrame({
    'country': ['USA', 'USA', 'USA'],
    'sport': ['football', 'golf', 'football'],
    'age': [21, 28, 20]
})
team_sports = ['football', 'basketball']

print(pd.DataFrame({
    'country': df['country'],
    'sport': df['sport'],
    'not_sorted': (~df['sport'].isin(team_sports)).cumsum(),
    'sorted': (~df['sport'].sort_values()
                           .isin(team_sports)).cumsum().sort_index()
}))
 
   country     sport  not_sorted  sorted
0     USA  football           0       0
1     USA      golf           1       1
2     USA  football           1       0  # football 1 (separate group if not sorted)
 

Сортировка гарантирует, что футбол будет идти вместе, поэтому этого не произойдет


Установка:

 import pandas as pd

df = pd.DataFrame({
    'country': ['USA', 'USA', 'USA', 'USA', 'China', 'China', 'China'],
    'sport': ['football', 'football', 'golf', 'golf', 'football', 'basketball',
              'basketball'],
    'age': [21, 28, 20, 44, 30, 22, 41]
})