今天,我们使用 Netflix 电影和电视节目数据集,来进行数据可视化,当然这是一个有趣的实战过程哦!
本文的重点就是使用 Matplotlib 来进行一种较为有趣的数据可视化
我们可以在 Python 最流行的数据可视化库 Matplotlib 中创建类似 xkcd 的绘图,并可以在这个项目中同 Matplotlib 可视化组合起来,让整个数据分析变得更有趣
下面我们先来看看数据吧
数据集
我们可以在 Kaggle 上找到 Netflix 数据集,截至 2020 年,已经包含 7787 部 Netflix 上可用的电影和电视节目的数据
下面就先查看数据
import pandas as pd import matplotlib.pyplot as plt plt.rcParams['figure.dpi'] = 200 df = pd.read_csv("../input/netflix-shows/netflix_titles.csv") df.head()
接下来我们向数据集当中增加一些新功能,我们后面使用
df["date_added"] = pd.to_datetime(df['date_added']) df['year_added'] = df['date_added'].dt.year.astype('Int64') df['month_added'] = df['date_added'].dt.month df['season_count'] = df.apply(lambda x : x['duration'].split(" ")[0] if "Season" in x['duration'] else "", axis = 1) df['duration'] = df.apply(lambda x : x['duration'].split(" ")[0] if "Season" not in x['duration'] else "", axis = 1) df.head()
下面我们就可以进入有趣的数据分析了
当然,如果要在 Matplotlib 中使用 XKCDify 可视化,还需要添加如下代码
with plt.xkcd():
1.Netflix 时间轴
我们先查看一个描述 Netflix 多年来演变的时间表
from datetime import datetime # these go on the numbers below tl_dates = [ "1997\nFounded", "1998\nMail Service", "2003\nGoes Public", "2007\nStreaming service", "2016\nGoes Global", "2021\nNetflix & Chill" ] tl_x = [1, 2, 4, 5.3, 8,9] # the numbers go on these tl_sub_x = [1.5,3,5,6.5,7] tl_sub_times = [ "1998","2000","2006","2010","2012" ] tl_text = [ "Netflix.com launched", "Starts\nPersonal\nRecommendations","Billionth DVD Delivery","Canadian\nLaunch","UK Launch"] with plt.xkcd(): # Set figure & Axes fig, ax = plt.subplots(figsize=(15, 4), constrained_layout=True) ax.set_ylim(-2, 1.75) ax.set_xlim(0, 10) # Timeline : line ax.axhline(0, xmin=0.1, xmax=0.9, c='deeppink', zorder=1) # Timeline : Date Points ax.scatter(tl_x, np.zeros(len(tl_x)), s=120, c='palevioletred', zorder=2) ax.scatter(tl_x, np.zeros(len(tl_x)), s=30, c='darkmagenta', zorder=3) # Timeline : Time Points ax.scatter(tl_sub_x, np.zeros(len(tl_sub_x)), s=50, c='darkmagenta',zorder=4) # Date Text for x, date in zip(tl_x, tl_dates): ax.text(x, -0.55, date, ha='center', fontfamily='serif', fontweight='bold', color='royalblue',fontsize=12) # Stemplot : vertical line levels = np.zeros(len(tl_sub_x)) levels[::2] = 0.3 levels[1::2] = -0.3 markerline, stemline, baseline = ax.stem(tl_sub_x, levels, use_line_collection=True) plt.setp(baseline, zorder=0) plt.setp(markerline, marker=',', color='darkmagenta') plt.setp(stemline, color='darkmagenta') # Text for idx, x, time, txt in zip(range(1, len(tl_sub_x)+1), tl_sub_x, tl_sub_times, tl_text): ax.text(x, 1.3*(idx%2)-0.5, time, ha='center', fontfamily='serif', fontweight='bold', color='royalblue', fontsize=11) ax.text(x, 1.3*(idx%2)-0.6, txt, va='top', ha='center', fontfamily='serif',color='royalblue') # Spine for spine in ["left", "top", "right", "bottom"]: ax.spines[spine].set_visible(False) # Ticks ax.set_xticks([]) ax.set_yticks([]) # Title ax.set_title("Netflix through the years", fontweight="bold", fontfamily='serif', fontsize=16, color='royalblue') ax.text(2.4,1.57,"From DVD rentals to a global audience of over 150m people - is it time for Netflix to Chill?", fontfamily='serif', fontsize=12, color='mediumblue') plt.show()
上图展示了 Netflix 旅程当中一幅相当不错的画面, 此外,由于 plt.xkcd() 函数,这幅图看起来是手绘的,看起来确实很棒!
2.电影和电视节目
接下来我们看一下电影与电视节目的比例
col = "type" grouped = df[col].value_counts().reset_index() grouped = grouped.rename(columns = {col : "count", "index" : col}) with plt.xkcd(): explode = (0, 0.1) # only "explode" the 2nd slice (i.e. 'TV Show') fig1, ax1 = plt.subplots(figsize=(5, 5), dpi=100) ax1.pie(grouped["count"], explode=explode, labels=grouped["type"], autopct='%1.1f%%', shadow=True, startangle=90) ax1.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle. plt.show()
平台上的电视节目数量不到总内容的三分之一, 所以,与 Netflix 上的电视节目相比,我们可能更有机会找到一部相对较好的电影。
3.内容最多的国家
这一次我们制作一个水平条形图,代表内容最多的前 25 个国家/地区。DataFrame 中的 country 列有几行包含 1 个以上的国家。
from collections import Counter col = "country" categories = ", ".join(df[col].fillna("")).split(", ") counter_list = Counter(categories).most_common(25) counter_list = [_ for _ in counter_list if _[0] != ""] labels = [_[0] for _ in counter_list] values = [_[1] for _ in counter_list] with plt.xkcd(): fig, ax = plt.subplots(figsize=(10, 10), dpi=100) y_pos = np.arange(len(labels)) ax.barh(y_pos, values, align='center') ax.set_yticks(y_pos) ax.set_yticklabels(labels) ax.invert_yaxis() # labels read top-to-bottom ax.set_xlabel('Content') ax.set_title('Countries with most content') plt.show()
看完上图后的一些总体看法:
- Netflix 上的绝大多数内容都来自美国
- 尽管 Netflix 在印度起步较晚(2016 年),但它已经排在美国之后的第二位。因此,印度是 Netflix 的一个大市场。
4.受欢迎的导演和演员
为了看看受欢迎的导演和演员,我们决定绘制一个图形,其中包含来自内容最多的前六个国家的六个子情节,并为每个子情节制作水平条形图。我们来看看下面的图
a.最受欢迎的导演
from collections import Counter from matplotlib.pyplot import figure import math colours = ["orangered", "mediumseagreen", "darkturquoise", "mediumpurple", "deeppink", "indianred"] countries_list = ["United States", "India", "United Kingdom", "Japan", "France", "Canada"] col = "director" with plt.xkcd(): figure(num=None, figsize=(20, 8)) x=1 for country in countries_list: country_df = df[df["country"]==country] categories = ", ".join(country_df[col].fillna("")).split(", ") counter_list = Counter(categories).most_common(6) counter_list = [_ for _ in counter_list if _[0] != ""] labels = [_[0] for _ in counter_list][::-1] values = [_[1] for _ in counter_list][::-1] if max(values)<10: values_int = range(0, math.ceil(max(values))+1) else: values_int = range(0, math.ceil(max(values))+1, 2) plt.subplot(2, 3, x) plt.barh(labels,values, color = colours[x-1]) plt.xticks(values_int) plt.title(country) x+=1 plt.suptitle('Popular Directors with the most content') plt.tight_layout() plt.show()
b.最受欢迎的演员
col = "cast" with plt.xkcd(): figure(num=None, figsize=(20, 8)) x=1 for country in countries_list: df["from_country"] = df['country'].fillna("").apply(lambda x : 1 if country.lower() in x.lower() else 0) small = df[df["from_country"] == 1] cast = ", ".join(small['cast'].fillna("")).split(", ") tags = Counter(cast).most_common(11) tags = [_ for _ in tags if "" != _[0]] labels, values = [_[0]+" " for _ in tags][::-1], [_[1] for _ in tags][::-1] if max(values)<10: values_int = range(0, math.ceil(max(values))+1) elif max(values)>=10 and max(values)<=20: values_int = range(0, math.ceil(max(values))+1, 2) else: values_int = range(0, math.ceil(max(values))+1, 5) plt.subplot(2, 3, x) plt.barh(labels,values, color = colours[x-1]) plt.xticks(values_int) plt.title(country) x+=1 plt.suptitle('Popular Actors with the most content') plt.tight_layout() plt.show()
5.一些最古老的电影和电视节目
先来查看最古老的电影
a.古老的电影
small = df.sort_values("release_year", ascending = True) small = small[small['duration'] != ""].reset_index() small[['title', "release_year"]][:15]
b.古老的电视节目
small = df.sort_values("release_year", ascending = True) small = small[small['season_count'] != ""].reset_index() small = small[['title', "release_year"]][:15] small
哇,Netflix 有一些非常老的电影和电视节目——有些甚至在 80 多年前就已经发行了, 你看过这些吗?
6.Netflix 有最新的内容吗?
是的,Netflix 确实很酷,而且拥有一个世纪前的内容,但它是否也有最新的电影和电视节目呢, 为了找到这一点,我们首先计算一下内容添加到 Netflix 的日期与该内容的发布年份之间的差异
df["year_diff"] = df["year_added"]-df["release_year"]
然后,我们创建了一个散点图,x 轴作为年份差异,y 轴作为电影/电视节目的数量:
col = "year_diff" only_movies = df[df["duration"]!=""] only_shows = df[df["season_count"]!=""] grouped1 = only_movies[col].value_counts().reset_index() grouped1 = grouped1.rename(columns = {col : "count", "index" : col}) grouped1 = grouped1.dropna() grouped1 = grouped1.head(20) grouped2 = only_shows[col].value_counts().reset_index() grouped2 = grouped2.rename(columns = {col : "count", "index" : col}) grouped2 = grouped2.dropna() grouped2 = grouped2.head(20) with plt.xkcd(): figure(num=None, figsize=(8, 5)) plt.scatter(grouped1[col], grouped1["count"], color = "hotpink") plt.scatter(grouped2[col], grouped2["count"], color = '#88c999') values_int = range(0, math.ceil(max(grouped1[col]))+1, 2) plt.xticks(values_int) plt.xlabel("Difference between the year when the content has been\n added on Netflix and the realease year") plt.ylabel("Number of Movies/TV Shows") plt.legend(["Movies", "TV Shows"]) plt.tight_layout() plt.show()
正如我们在上面的可视化中看到的,Netflix 上的大部分内容都是在发布之日起一年内添加的。因此,Netflix 在大多数情况下确实拥有最新的内容!
下面轻松一下,看一个 xkcd 漫画给你,然后继续
7. Netflix 专注于什么样的内容?
我们继续浏览评级栏并比较 Netflix 为儿童、青少年和成人制作的内容量——以及这些年来他们的重点是否从一个群体转移到另一个群体。
我们首先查看了 DataFrame 中的独特评级:
print(df['rating'].unique()) output: ['TV-MA' 'R' 'PG-13' 'TV-14' 'TV-PG' 'NR' 'TV-G' 'TV-Y' nan 'TV-Y7' 'PG' 'G' 'NC-17' 'TV-Y7-FV' 'UR']
然后,我们根据他们所属的组(即小孩子、大孩子、青少年和成熟)对评级进行分类,并将评级列中的值更改为他们的组名称
ratings_list = ['TV-MA', 'R', 'PG-13', 'TV-14', 'TV-PG', 'TV-G', 'TV-Y', 'TV-Y7', 'PG', 'G', 'NC-17', 'TV-Y7-FV'] ratings_group_list = ['Little Kids', 'Older Kids', 'Teens', 'Mature'] ratings_dict={ 'TV-G': 'Little Kids', 'TV-Y': 'Little Kids', 'G': 'Little Kids', 'TV-PG': 'Older Kids', 'TV-Y7': 'Older Kids', 'PG': 'Older Kids', 'TV-Y7-FV': 'Older Kids', 'PG-13': 'Teens', 'TV-14': 'Teens', 'TV-MA': 'Mature', 'R': 'Mature', 'NC-17': 'Mature' } for rating_val, rating_group in ratings_dict.items(): df.loc[df.rating == rating_val, "rating"] = rating_group
最后,我们绘制了 x 轴为年份、y 轴为内容计数的线图
df['rating_val']=1 x=0 labels=['kinda\nless', 'not so\nbad', 'holyshit\nthat\'s too\nmany'] with plt.xkcd(): for r in ratings_group_list: grouped = df[df['rating']==r] year_df = grouped.groupby(['year_added']).sum() year_df.reset_index(level=0, inplace=True) plt.plot(year_df['year_added'], year_df['rating_val'], color=colours[x], marker='o') values_int = range(2008, math.ceil(max(year_df['year_added']))+1, 2) plt.yticks([200, 600, 1000], labels) plt.xticks(values_int) plt.title('Count of shows and movies that Netflix\n has been producing for different audiences', fontsize=12) plt.xlabel('Year', fontsize=14) plt.ylabel('Content Count', fontsize=14) x+=1 plt.legend(ratings_group_list) plt.tight_layout() plt.show()
这个可视化中的数据向我们展示了 Netflix 上成熟观众的内容数量远高于其他群体。另一个有趣的观察结果是,从 2019 年到 2020 年,为小孩子制作的内容数量激增,而在此期间,为大孩子、青少年和成熟观众制作的内容数量有所减少
8. 热门类型(国家)
col = "listed_in" colours = ["violet", "cornflowerblue", "darkseagreen", "mediumvioletred", "blue", "mediumseagreen", "darkmagenta", "darkslateblue", "seagreen"] countries_list = ["United States", "India", "United Kingdom", "Japan", "France", "Canada", "Spain", "South Korea", "Germany"] with plt.xkcd(): figure(num=None, figsize=(20, 8)) x=1 for country in countries_list: df["from_country"] = df['country'].fillna("").apply(lambda x : 1 if country.lower() in x.lower() else 0) small = df[df["from_country"] == 1] genre = ", ".join(small['listed_in'].fillna("")).split(", ") tags = Counter(genre).most_common(3) tags = [_ for _ in tags if "" != _[0]] labels, values = [_[0]+" " for _ in tags][::-1], [_[1] for _ in tags][::-1] if max(values)>200: values_int = range(0, math.ceil(max(values)), 100) elif max(values)>100 and max(values)<=200: values_int = range(0, math.ceil(max(values))+50, 50) else: values_int = range(0, math.ceil(max(values))+25, 25) plt.subplot(3, 3, x) plt.barh(labels,values, color = colours[x-1]) plt.xticks(values_int) plt.title(country) x+=1 plt.suptitle('Top Genres') plt.tight_layout() plt.show()
从上图可以看出:
- 戏剧和喜剧是几乎每个国家最受欢迎的类型
- 日本看了很多动漫
- 浪漫的电视节目和电视剧在韩国很受欢迎
- 儿童和家庭电影是加拿大第三大流行类型
9. 词云
我们最终用两个词云结束了这个项目——第一个是描述列的词云,第二个是标题列的词云。
a.描述列的词云
from wordcloud import WordCloud import random from PIL import Image import matplotlib # Custom colour map based on Netflix palette cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", ['#221f1f', '#b20710']) text = str(list(df['description'])).replace(',', '').replace('[', '').replace("'", '').replace(']', '').replace('.', '') mask = np.array(Image.open('../input/finallogo/New Note.png')) wordcloud = WordCloud(background_color = 'white', width = 500, height = 200,colormap=cmap, max_words = 150, mask = mask).generate(text) plt.figure( figsize=(5,5)) plt.imshow(wordcloud, interpolation = 'bilinear') plt.axis('off') plt.tight_layout(pad=0) plt.show()
Live、love、life、friend、family、world 和 find 是一些最常出现在电影和节目描述中的词,另一件有趣的事情是词——一、二、三和四——都出现在词云中
b.标题的词云
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", ['#221f1f', '#b20710']) text = str(list(df['title'])).replace(',', '').replace('[', '').replace("'", '').replace(']', '').replace('.', '') mask = np.array(Image.open('../input/finallogo/New Note.png')) wordcloud = WordCloud(background_color = 'white', width = 500, height = 200,colormap=cmap, max_words = 150, mask = mask).generate(text) plt.figure( figsize=(5,5)) plt.imshow(wordcloud, interpolation = 'bilinear') plt.axis('off') plt.tight_layout(pad=0) plt.show()
我们看到圣诞节就在这个词云的中心, Netflix 上似乎有很多圣诞电影。其他流行的词是爱、世界、男人、生活、故事、现场、秘密、女孩、男孩、美国人、游戏、夜晚、最后、时间和白天。
我们只花了几个小时就完成了这个项目,现在我们能够以全新的方式看待 Netflix 所做的一切。
希望这个项目和所有使用 Matplotlib 的有趣数据可视化示例也能给大家的项目带来灵感。
好了,今天的分享就到这里了,喜欢就给个“在看”吧!