Вы можете использовать это решение oneline (экранирование строк только для красивого формата):
movies = \
(movies.set_index(mv.columns.drop('genres',1).tolist())
.genres.str.split('|',expand=True)
.stack()
.reset_index()
.rename(columns={0:'genre'})
.loc[:,['genre','score','votes']]
.groupby('genre').agg({'score':['mean'], 'votes':['sum']})
)
score votes
mean sum
genre
Action 8.425714 7912508
Adventure 8.430000 7460632
Animation 8.293333 1769806
Biography 8.393750 2112875
Comedy 8.341509 3166269
...
ОБЪЯСНЕНИЕ :
Основная проблемаэто кратные True
значения, полученные в результате процесса one_hot_encoding
по жанрам.Один фильм можно назначить одному или нескольким жанрам.Следовательно, вы не можете использовать методы агрегирования по жанрам.С другой стороны, использование поля genres
в том виде, в каком оно есть, приведет к растворению результатов нескольких полов, как показано в вашем вопросе:
genres
Action 5.837500
Action|Adventure 6.152381
Action|Adventure|Animation|Comedy|Family|Fantasy 7.500000
Action|Adventure|Animation|Family|Fantasy|Sci-Fi 6.100000
Action|Adventure|Biography|Crime|History|Western 6.300000
Action|Adventure|Biography|Drama|History 7.700000
Обходной путь - дублирование строк, если найдено более одного пола.Используя комбинацию split
с методом expand
, установленным на True
, вы можете создать несколько фреймов данных и затем разместить их в стеке.Например, фильм с двумя жанрами появится в двух результирующих кадрах данных, где каждый кадр данных представляет фильмы, назначенные каждому жанру.Наконец, после разбора вы можете агрегировать по полу с несколькими функциями.Я объясню шаг за шагом:
1.Получить 250 лучших фильмов (по баллу)
Загрузить данные:
import pandas as pd
import numpy as np
headers = ['imdbID', 'title', 'year', 'score', 'votes', 'runtime', 'genres']
movies = pd.read_csv("imdb_top_10000.txt", sep="\t", header=None, names=headers, encoding='UTF-8')
Обратите внимание, что в поле genres
есть нулевые значения:
imdbID title year score votes runtime genres
7917 tt0990404 Chop Shop (2007) 2007 7.2 2104 84 mins. NaN
Поскольку методы агрегированияс Pandas пропустит строки с любым нулевым значением, и у нас есть только 1 фильм с нулевым значением в этом поле, который можно установить вручную (проверено на Imdb):
movies.loc[movies.genres.isnull(),"genres"] = "Drama"
Теперь, как вы уже показали,нам нужны лучшие 250 фильмов по баллу:
movies = movies.sort_values('score', ascending=False).head(250)
2.Создать жанровое поле из жанров, используя split с расширением
2.1.Установить индекс
В качестве столбца оставить только поле жанров, остальные поля - индексом.Это для удобства работы над жанрами.
movies = movies.set_index(movies.columns.drop('genres',1).tolist())
genres
imdbID title year score votes runtime
tt0111161 The Shawshank Redemption (1994) 1994 9.2 619479 142 mins. Crime|Drama
tt0068646 The Godfather (1972) 1972 9.2 474189 175 mins. Crime|Drama
tt0060196 The Good, the Bad and the Ugly (1966) 1966 9.0 195238 161 mins. Western
tt0110912 Pulp Fiction (1994) 1994 9.0 490065 154 mins. Crime|Thriller
tt0252487 Outrageous Class (1975) 1975 9.0 9823 87 mins. Comedy|Drama
(250, 1)
2.2.Разделить по жанрам
Это создаст N кадров данных из N итераций разделения.
movies = movies.genres.str.split('|',expand=True)
0 \
imdbID title year score votes runtime
tt0111161 The Shawshank Redemption (1994) 1994 9.2 619479 142 mins. Crime
tt0068646 The Godfather (1972) 1972 9.2 474189 175 mins. Crime
tt0060196 The Good, the Bad and the Ugly (1966) 1966 9.0 195238 161 mins. Western
tt0110912 Pulp Fiction (1994) 1994 9.0 490065 154 mins. Crime
tt0252487 Outrageous Class (1975) 1975 9.0 9823 87 mins. Comedy
1 \
imdbID title year score votes runtime
tt0111161 The Shawshank Redemption (1994) 1994 9.2 619479 142 mins. Drama
tt0068646 The Godfather (1972) 1972 9.2 474189 175 mins. Drama
tt0060196 The Good, the Bad and the Ugly (1966) 1966 9.0 195238 161 mins. None
tt0110912 Pulp Fiction (1994) 1994 9.0 490065 154 mins. Thriller
tt0252487 Outrageous Class (1975) 1975 9.0 9823 87 mins. Drama
...
2.3.Стек
Теперь у вас есть уникальное значение жанра для каждого фильма, где фильм может иметь более 1 строки, если было назначено более 1 жанра, вы можете сложить набор фреймов данных.Обратите внимание, что теперь у нас более 250 строк (662 строки), но мы имеем 250 отдельных фильмов.
movies = movies.stack()
imdbID title year score votes runtime
tt0111161 The Shawshank Redemption (1994) 1994 9.2 619479 142 mins. 0 Crime
1 Drama
tt0068646 The Godfather (1972) 1972 9.2 474189 175 mins. 0 Crime
1 Drama
tt0060196 The Good, the Bad and the Ugly (1966) 1966 9.0 195238 161 mins. 0 Western
dtype: object
(662,)
3.Parse
Получите подходящую структуру данных перед агрегированием:
# Multiple index to columns
movies = movies.reset_index()
# Name the new column for genre
movies = movies.rename(columns={0:'genre'})
# Only wanted fields to be aggregated
movies = movies.loc[:,['genre','score','votes']]
genre score votes
0 Crime 9.2 619479
1 Drama 9.2 619479
2 Crime 9.2 474189
3 Drama 9.2 474189
4 Western 9.0 195238
(662, 3)
4.Агрегат
По вашему запросу, оценка должна быть агрегирована по среднему значению, а количество голосов по сумме:
movies = movies.groupby('genres').agg({'score':['mean'], 'votes':['sum']})
score votes
mean sum
genre
Action 8.425714 7912508
Adventure 8.430000 7460632
Animation 8.293333 1769806
Biography 8.393750 2112875
Comedy 8.341509 3166269
(21, 2)