本文概述
Netflix最近发布了一些用户评分数据。我想问一个简单的问题:Netflix订户更喜欢旧电影还是新电影?
直观地讲, 你希望将数据集分成几组, 每年一组, 然后计算一个汇总统计量, 例如平均值或中位数, 然后查看该统计量是否随着时间的推移而增加(此后, 你可能想要进行统计检验)。
很棒的是, 存在一个用于执行和考虑此操作的概念框架, 以及Python和R中的实现。该框架被称为” split-apply-combine”, 因为我们…
- 步骤1:通过从原始DataFrame创建groupby对象将数据分为几组;
- 步骤2:应用一个函数, 在这种情况下, 应用一个聚合函数来计算摘要统计信息(你也可以在此步骤中转换或过滤数据);
- 步骤3:将结果合并到一个新的DataFrame中。
这是手头分析的概念框架。在本文中, 你将学习如何使用Python软件包pandas回答上述Netflix分级问题。你可以使用例如dplyr包在R中执行相同的操作。我还必须深入研究groupby对象, 但它们并不是最直观的对象。正如我们将看到的, 使用groupby对象进行拆分应用合并的过程是一种我们都直观地执行的模式, 但是Hadley Wickham在2011年通过其论文《数据分析的拆分应用合并策略》使该过程正式化。 。
如果你发现这项技术很有用, 则可以(在许多其他事情中)进一步了解它, 并在我们的”用pandas操作数据帧”课程中进行实践。
pandas数据探索
汇入资料
在这里, 你将使用pandas, groupby对象和split-apply-combine的原理来检查Netflix电影范围根据发行年份的不同而变化。我最初是在这里遇到data.world上的数据的, 你也可以在这里在概念中心找到它。如果你想重现它, 可以在这里找到这篇文章中的所有代码。
你将首先导入必要的包和数据, 并检出数据的前五行:
# Import packages and set visualization style
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
%matplotlib inline
# Import data and check out head of DataFrame
df = pd.read_csv('data/chasewillden-netflix-shows/data/netflix.csv')
df.head()
标题 | 评分 | 等级 | 评级说明 | release_year | user_rating_score | user_rating_size | |
---|---|---|---|---|---|---|---|
0 | 白小鸡 | PG-13 | 粗俗和性幽默, 语言和一些毒品… | 80 | 2004 | 82.0 | 80 |
1 | 幸运数字Slevin | [R | 强烈的暴力, 性内容和成人语言… | 100 | 2006 | NaN | 82 |
2 | 实习医生格蕾 | TV-14 | 父母强烈警告。可能不合适… | 90 | 2016 | 98.0 | 80 |
3 | 越狱 | TV-14 | 父母强烈警告。可能不合适… | 90 | 2008 | 98.0 | 80 |
4 | 老爸老妈的浪漫史 | 电视PG | 建议家长指导。可能不适合… | 70 | 2014 | 94.0 | 80 |
在我看来, 这很酷:你可以找到标题, 等级, 发行年份和用户等级评分, 以及其他几列。在执行groupby和split-apply-combine过程之前, 让我们仔细看一下数据以确保它是我们认为的样子并处理缺失值。请注意, 第二行(行1)的user_rating_score中缺少值NaN。
用图和统计汇总数据
pandasDataFrame .info()方法是无价的。在下面应用它显示你有1000行和7列数据, 而且感兴趣的列user_rating_score只有605个非空值。这意味着有395个缺失值:
# Check out info of DataFrame
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 7 columns):
title 1000 non-null object
rating 1000 non-null object
ratinglevel 941 non-null object
ratingdescription 1000 non-null int64
release_year 1000 non-null int64
user_rating_score 605 non-null float64
user_rating_size 1000 non-null int64
dtypes: float64(1), int64(3), object(3)
memory usage: 54.8+ KB
你可以删除具有任何缺失值的行, 删除任何重复的行并使用seaborn构建DataFrame的成对图, 以便获得数据的视觉感。你将通过”评分”列为数据着色。查看这些图, 看看可以从中获得什么信息。
# Drop rows with missing values and drop duplicate
df.dropna(inplace=True)
df.drop_duplicates(inplace=True)
# Visualize pairplot of df
sns.pairplot(df, hue='rating');
签出例如user_rating_score作为release_year的函数。没有视觉上可识别的趋势, 但是也许某些数据分析将得出任何趋势。如果你想查看DataFrame的几个摘要统计信息, 也可以使用.describe()方法执行此操作:
# Get summary stats of df
df.describe()
评级说明 | release_year | user_rating_score | user_rating_size | |
---|---|---|---|---|
计数 | 246.000000 | 246.000000 | 246.000000 | 246.0 |
意思 | 73.556911 | 2010.272358 | 81.390244 | 80.0 |
小时 | 26.616145 | 8.887219 | 12.677883 | 0.0 |
我 | 10.000000 | 1940.000000 | 55.000000 | 80.0 |
25% | 60.000000 | 2007.000000 | 71.000000 | 80.0 |
50% | 80.000000 | 2015.000000 | 83.500000 | 80.0 |
75% | 90.000000 | 2016.000000 | 92.750000 | 80.0 |
最大值 | 124.000000 | 2017.000000 | 99.000000 | 80.0 |
Groupbys和split-apply-combine回答问题
步骤1.分割
既然你已经签出了数据, 那么现在该是有趣的部分了。首先, 你将使用groupby方法将数据分成几组, 其中每一组都是给定年份发行的电影。这是split-apply-combine中的拆分:
# Group by year
df_by_year = df.groupby('release_year')
这将创建一个groupby对象:
# Check type of GroupBy object
type(df_by_year)
pandas.core.groupby.DataFrameGroupBy
步骤2.申请
这样的分组对象非常有用。还记得DataFrame的.describe()方法返回数字列的摘要统计信息吗?好了, DataFrameGroupBy对象的.describe()方法返回每个数字列的摘要统计信息, 但为拆分中的每个组计算摘要统计信息。就你而言, 适用于每个release_year。这是split-apply-combine中的apply的示例:你将.describe()方法应用于groupby中的每个组。这样做并打印结果的前5行:
# Summary stats over years
df_by_year.describe().head()
评级说明 | user_rating_score | user_rating_size | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
计数 | 意思 | 小时 | 我 | 25% | 50% | 75% | 最大值 | 计数 | 意思 | … | 75% | 最大值 | 计数 | 意思 | 小时 | 我 | 25% | 50% | 75% | 最大值 | |
release_year | |||||||||||||||||||||
1940 | 1.0 | 35.0 | NaN | 35.0 | 35.0 | 35.0 | 35.0 | 35.0 | 1.0 | 61.0 | … | 61.0 | 61.0 | 1.0 | 80.0 | NaN | 80.0 | 80.0 | 80.0 | 80.0 | 80.0 |
1978 | 1.0 | 60.0 | NaN | 60.0 | 60.0 | 60.0 | 60.0 | 60.0 | 1.0 | 86.0 | … | 86.0 | 86.0 | 1.0 | 80.0 | NaN | 80.0 | 80.0 | 80.0 | 80.0 | 80.0 |
1982 | 1.0 | 60.0 | NaN | 60.0 | 60.0 | 60.0 | 60.0 | 60.0 | 1.0 | 68.0 | … | 68.0 | 68.0 | 1.0 | 80.0 | NaN | 80.0 | 80.0 | 80.0 | 80.0 | 80.0 |
1986 | 1.0 | 35.0 | NaN | 35.0 | 35.0 | 35.0 | 35.0 | 35.0 | 1.0 | 67.0 | … | 67.0 | 67.0 | 1.0 | 80.0 | NaN | 80.0 | 80.0 | 80.0 | 80.0 | 80.0 |
1987 | 1.0 | 60.0 | NaN | 60.0 | 60.0 | 60.0 | 60.0 | 60.0 | 1.0 | 58.0 | … | 58.0 | 58.0 | 1.0 | 80.0 | NaN | 80.0 | 80.0 | 80.0 | 80.0 | 80.0 |
5行×24列
如果要查看分组的外观, 可以将groupby对象传递给函数list():
# Cast grouping as a list and check out one year
list(df_by_year)[10]
(1995, title rating ratinglevel \
766 Balto G General Audiences. Suitable for all ages.
967 Heavyweights PG some rude language and pranks
ratingdescription release_year user_rating_score user_rating_size
766 35 1995 64.0 80
967 60 1995 74.0 80 )
步骤3.合并
假设你想要每年的平均值或中位数user_rating_score。然后, 你可以将.mean()或.median()方法分别应用于groupby对象, 并将它们”组合”到新的DataFrame中。
# Get median values by year and print first 5 rows
df_med_by_year = df_by_year.median()
df_med_by_year.head()
评级说明 | user_rating_score | user_rating_size | |
---|---|---|---|
release_year | |||
1940 | 35.0 | 61.0 | 80.0 |
1978 | 60.0 | 86.0 | 80.0 |
1982 | 60.0 | 68.0 | 80.0 |
1986 | 35.0 | 67.0 | 80.0 |
1987 | 60.0 | 58.0 | 80.0 |
关于DataFrame df_med_by_year的索引有一个重要的细微之处。回想一下, DataFrame的索引由行标签组成。签出原始DataFrame df的索引:
# Print index of df
print(df.index)
Int64Index([ 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...
908, 911, 917, 931, 962, 966, 967, 972, 973, 979], dtype='int64', length=246)
该索引由原始行号组成, 并用整数标记。当你在上方放置一些行时, 缺少” 1″。 df_med_by_year的索引由你分组的原始列中的值组成, 即release_year以来的年份:
# Print index
print(df_med_by_year.index)
Int64Index([1940, 1978, 1982, 1986, 1987, 1989, 1990, 1992, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017], dtype='int64', name='release_year')
你对user_rating_score列感兴趣, 该列包含每年的平均评分。你可以切出df_med_by_year的user_rating_score列, 并将其绘制为年份的函数(由DataFrame df_rat_by_year的索引给出):
# Slice out user rating and plot
df_rat_by_year = df_med_by_year['user_rating_score']
plt.scatter(df_rat_by_year.index, df_rat_by_year)
plt.xlabel('year of release')
plt.ylabel('median rating');
从图中可以看出, 中位数评分肯定会随着时间的推移而增加。你需要利用一些更复杂的统计数据来使我总体上相信这种趋势, 但是”探索性数据分析”示例是进一步研究的一个很好的起点。
日常使用Groupbys和split-app-combine
Groupby对象不直观。但是, 它们确实对应于将数据集相对于其一列(或多于一列)进行拆分的自然行为, 但让我们将其保存在另一篇关于按多列和层次结构索引进行分组的文章中。
拆分应用合并原则不仅优雅实用, 而且数据科学家每天都会使用它, 就像上面的示例一样。要了解其更多用途, 请查看Hadley Wickham的原始论文《数据分析的拆分应用组合策略》。如果你有任何想法, 回应和/或反省, 请随时通过twitter @ hugobowne与我联系。
评论前必须登录!
注册