#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]
})