介绍
在数据科学和分析的领域中,数据的力量不仅在于提取洞察,还在于有效地传达这些洞察;这就是数据可视化发挥作用的地方。
数据可视化是信息和数据的图形化表示。它使用图表、图形和地图等视觉元素,使原始数据中的模式、趋势和异常更容易被看到。对于数据科学家和分析师来说,数据可视化是一种重要的工具,有助于更快、更准确地理解数据,支持基于数据的叙事,并帮助做出数据驱动的决策。
在本文中,您将学习使用Python和Dash框架创建一个可视化Netflix内容分发和分类的仪表盘。
什么是Dash?
Dash是一个由Plotly开发的开源低代码框架,用于纯Python创建分析型Web应用程序。传统上,为了实现这样的目的,您可能需要使用JavaScript和HTML,这要求您同时具备后端(Python)和前端(JavaScript、HTML)技术的专业知识。
然而,Dash弥合了这个差距,使数据科学家和分析师能够仅使用Python构建交互式、美观的仪表盘。这种低代码开发的特点使得Dash成为创建分析型仪表盘的合适选择,特别适合那些主要使用Python的用户。
数据集分析
现在您已经了解了Dash,让我们开始实际项目。您将使用Kaggle上的Netflix电影和电视节目数据集,该数据集由Shivam Bansal创建。
该数据集包含有关Netflix上可用电影和电视节目的详细信息,例如内容类型、标题、导演、演员阵容、制作国家、发布年份、评级、持续时间等。
尽管这个数据集是在2021年创建的,但它仍然是一个发展数据可视化技能和了解媒体娱乐趋势的宝贵资源。
使用这个数据集,您将旨在创建一个仪表盘,以实现以下几点的可视化:
- 地理内容分布:展示不同国家在不同年份的内容制作变化的地图图表。
- 内容分类:将Netflix的内容分为电视节目和电影,以查看哪些类型最为突出。
设置项目工作空间
让我们开始创建一个名为netflix-dashboard的项目目录,然后通过以下命令初始化和激活Python虚拟环境:
# Linux & MacOS
mkdir netflix-dashboard && cd netflix-dashboard
python3 -m venv netflix-venv && source netflix-venv/bin/activate
# Windows Powershell
mkdir netflix-dashboard && cd netflix-dashboard
python -m venv netflix-venv && .\netflix-venv\Scripts\activate
接下来,您需要安装一些外部包。您将使用pandas
进行数据处理,dash
创建仪表盘,plotly
创建图表,dash-bootstrap-components
为仪表盘添加一些样式:
# Linux & MacOS
pip3 install pandas dash plotly dash-bootstrap-components
# Windows Powershell
pip install pandas dash plotly dash-bootstrap-components
数据集清洗
在Netflix的数据集中,你会发现 director
, cast
,和 country
列中存在缺失值。另外,为了方便分析,将 date_added
列的 string
值转换为 datetime
类型也是很方便的。
为了清洗数据集,你可以创建一个新的文件 clean_netflix_dataset.py,包含以下代码,然后运行它:
import pandas as pd
# 加载数据集
df = pd.read_csv('netflix_titles.csv')
# 填充缺失值
df['director'].fillna('无导演', inplace=True)
df['cast'].fillna('无演员', inplace=True)
df['country'].fillna('无国家', inplace=True)
# 删除缺失值和重复值
df.dropna(inplace=True)
df.drop_duplicates(inplace=True)
# 去除 `date_added` 列中的空格,并将值转换为 `datetime` 类型
df['date_added'] = pd.to_datetime(df['date_added'].str.strip())
# 保存清洗后的数据集
df.to_csv('netflix_titles.csv', index=False)
开始使用Dash
在设置好工作空间并清洗数据集之后,你已经可以开始开发仪表盘了。创建一个新文件 app.py,包含以下代码:
from dash import Dash, dash_table, html
import pandas as pd
# 初始化一个Dash应用
app = Dash(__name__)
# 定义应用的布局
app.layout = html.Div([
html.H1('Netflix 电影和电视节目仪表盘'),
html.Hr(),
])
# 在本地开发模式下启动Dash应用
if __name__ == '__main__':
app.run_server(debug=True)
让我们来解析一下 app.py 中的代码:
app = Dash(__name__)
:这一行代码初始化一个新的Dash应用。可以将其视为应用的基础。app.layout = html.Div(…)
:app.layout
属性允许你编写类似HTML的代码来设计应用的用户界面。上面的布局使用了一个html.H1(…)
标题元素作为仪表盘的标题,并在标题下方使用了一个水平线html.Hr()
元素。app.run(debug=True)
:这一行代码启动一个开发服务器,以本地开发模式提供Dash应用。Dash使用轻量级的Web服务器框架Flask来将你的应用提供给Web浏览器。
运行 app.py 后,你会在终端中看到一条消息,指示你的Dash应用正在运行,并可以通过http://127.0.0.1:8050/在Web浏览器中访问它:
结果看起来很简单,对吧?别担心!这一节旨在展示最基本的Dash应用结构和组件。很快你将添加更多功能和组件,使其成为一个很棒的仪表盘!
添加Dash Bootstrap组件
下一步是编写仪表盘的布局代码并为其添加一些样式!为此,你可以使用Dash Bootstrap组件(DBC),这是一个提供Bootstrap组件给Dash的库,使你能够开发具有响应式布局的样式化应用。
仪表盘将使用一个 标签布局,这种布局提供了一种紧凑的方式来组织同一空间内的不同类型的信息。每个标签将对应一个独立的可视化。
让我们继续修改 app.py 的内容以使用DBC:
from dash import Dash,dcc, html
import pandas as pd
import dash_bootstrap_components as dbc
# 初始化Dash应用并导入Bootstrap主题以样式化仪表盘
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Container(
[
dcc.Store(id='store'),
html.H1('Netflix 电影和电视节目仪表盘'),
html.Hr(),
dbc.Tabs(
[
dbc.Tab(label='地理内容分布', tab_id='tab1'),
dbc.Tab(label='内容分类', tab_id='tab2'),
],
id='tabs',
active_tab='tab1',
),
html.Div(id='tab-content', className='p-4'),
]
)
if __name__ == '__main__':
app.run(debug=True)
在这个修改后的布局中,您将看到新的组件:
dbc.Container
:使用dbc.Container
作为顶级组件,将整个仪表板布局包装在一个响应式和灵活的容器中。dcc.Store
:这是Dash核心组件,允许您在客户端(用户的浏览器)上存储数据,通过在本地存储数据来提高应用程序的性能。dbc.Tabs
和dbc.Tab
:每个dbc.Tab
表示一个单独的选项卡,其中包含不同的可视化。属性label
是选项卡本身显示的内容,而tab_id
用于标识选项卡。属性active_tab
用于指定Dash应用程序启动时的活动选项卡。
现在运行app.py。生成的仪表板现在具有基于Bootstrap的布局,并且有两个空选项卡:
干得好!您终于可以向仪表板添加可视化了。
添加回调和可视化
在使用Dash时,通过回调函数实现交互性。回调函数是在输入属性更改时自动调用的函数。它被命名为“回调”,因为每当应用程序发生更改时,Dash都会“回调”调用该函数。
在此仪表板中,您将使用回调函数在所选选项卡中呈现相关的可视化,并将每个可视化存储在自己的Python文件中,该文件位于新的components目录下,以实现项目结构的更好组织和模块化。
地理内容分布可视化
让我们创建一个名为components的新目录,在其中创建geographical_content.py文件,该文件将生成一个分级地图,展示Netflix的内容在不同国家的年度变化情况:
import pandas as pd
import plotly.express as px
from dash import dcc, html
df = pd.read_csv('netflix_titles.csv')
# 过滤掉没有国家信息的条目,如果存在多个制片国家,则将第一个国家视为制片国家
df['country'] = df['country'].str.split(',').apply(lambda x: x[0].strip() if isinstance(x, list) else None)
# 从date_added列中提取年份
df['year_added'] = pd.to_datetime(df['date_added']).dt.year
df = df.dropna(subset=['country', 'year_added'])
# 计算每个国家每年产生的内容数量
df_counts = df.groupby(['country', 'year_added']).size().reset_index(name='count')
# 按'year_added'对DataFrame进行排序,以确保动画帧按升序排列
df_counts = df_counts.sort_values('year_added')
# 创建带有年份滑块的分级地图
fig1 = px.choropleth(df_counts,
locations='country',
locationmode='country names',
color='count',
hover_name='country',
animation_frame='year_added',
projection='natural earth',
title='各国逐年产生的内容',
color_continuous_scale='YlGnBu',
range_color=[0, df_counts['count'].max()])
fig1.update_layout(width=1280, height=720, title_x=0.5)
# 计算每年按类型产生的内容数量,并为缺失的类型-年份组合填充零
df_year_counts = df.groupby(['year_added', 'type']).size().reset_index(name='count')
# 使用plotly express创建折线图
fig2 = px.line(df_year_counts, x='year_added', y='count', color='type',
title='各年份按类型分布的内容',
markers=True, color_discrete_map={'Movie': 'dodgerblue', 'TV Show': 'darkblue'})
fig2.update_traces(marker=dict(size=12))
fig2.update_layout(width=1280, height=720, title_x=0.5)
layout = html.Div([
dcc.Graph(figure=fig1),
html.Hr(),
dcc.Graph(figure=fig2)
])
以上代码对数据进行了过滤和分组,按照 ‘country’ 和 ‘year_added’ 对数据进行分组,并在 df_counts DataFrame 中计算出每个国家每年所产生内容的数量。
然后,px.choroplet 函数根据 df_counts DataFrame 中的列构建地图图形,将其作为参数的值:
- locations=’country’:允许您指定包含在 ‘country’ 列中的地理位置值。
- locationmode=’country names’:该参数“告诉函数”所提供的 locations 是国家名称,因为 Plotly Express 还支持其他位置模式,如 ISO-3 国家代码或美国州。
- color=’count’:用于指定用于着色地图的数值数据。在这里,它指的是包含每个国家每年所产生内容数量的 ‘count’ 列。
- color_continous_scale=’YlGnBu’:当由 color 所表示的列包含数值数据时,为地图中的每个国家构建连续的颜色比例尺。
- animation_frame=’year_added’:该参数在 ‘year_added’ 列上创建动画。它在地图图形中添加了一个年份滑块,允许您查看每个国家年复年的内容产量演变的动画。
- projection=’natural earth’:该参数不使用 df_counts DataFrame 中的任何列;然而,需要设置 projection 为 ‘natural earth’,以使用地球世界地图。
在地图下方,还包括一个带有标记的线图,展示按类型(电视剧或电影)分类的内容量随时间的变化。
要生成该线图,创建了一个新的 DataFrame df_year_counts,它按 ‘year_added’ 和 ‘type’ 列对原始 df 数据进行分组,并计算每个组合的内容数量。
然后,将该分组数据与 px.line 结合使用,其中 ‘x’ 和 ‘y’ 参数分别分配给 ‘year_added’ 和 ‘count’ 列,’color’ 参数设置为 ‘type’,以区分电视剧和电影。
内容分类可视化
下一步是创建一个名为 content_classification.py 的新文件,该文件将生成一个树状图,以从类型和流派的角度可视化 Netflix 的内容:
import pandas as pd
import plotly.express as px
from dash import dcc, html
df = pd.read_csv('netflix_titles.csv')
# 将 listed_in 列进行拆分并展开以处理多个流派
df['listed_in'] = df['listed_in'].str.split(', ')
df = df.explode('listed_in')
# 计算每种类型和流派组合的数量
df_counts = df.groupby(['type', 'listed_in']).size().reset_index(name='count')
fig = px.treemap(df_counts, path=['type', 'listed_in'], values='count', color='count',
color_continuous_scale='Ice', title='按类型和流派的内容')
fig.update_layout(width=1280, height=960, title_x=0.5)
fig.update_traces(textinfo='label+percent entry', textfont_size=14)
layout = html.Div([
dcc.Graph(figure=fig),
])
在上述代码中,在加载数据后,对 ‘listed_in’ 列进行调整以处理每个内容的多个流派,通过拆分和展开流派,为每个内容的每个流派创建新行。
接下来,创建了 df_counts DataFrame,将数据按 ‘type’ 和 ‘listed_in’ 列分组,并计算每种类型-流派组合的数量。
然后,将df_counts
DataFrame的列用作px.treemap
函数参数的值,如下所示:
path=['type', 'listed_in']
:这些是在树状图中表示的分层类别。’type’和’listed_in’列分别包含内容类型(电视节目或电影)和流派。values='count'
:树状图中每个矩形的大小对应于’count’列,表示每个类型-流派组合的内容数量。color='count'
:’count’列也用于给树状图中的矩形上色。color_continuous_scale='Ice'
:当由color
指示的列包含数值数据时,在树状图中为每个矩形构建连续的颜色尺度。
创建完这两个新的可视化文件后,您的当前项目结构应该如下所示:
netflix-dashboard
├── app.py
├── clean_netflix_dataset.py
├── components
│ ├── content_classification.py
│ └── geographical_content.py
├── netflix-venv
│ ├── bin
│ ├── etc
│ ├── include
│ ├── lib
│ ├── pyvenv.cfg
│ └── share
└── netflix_titles.csv
实现回调函数
最后一步是修改app.py,导入components目录中的两个新可视化,并实现回调函数,在选择选项卡时渲染图形:
from dash import Dash, dcc, html, Input, Output
import dash_bootstrap_components as dbc
from components import (
geographical_content,
content_classification
)
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Container(
[
dcc.Store(id='store'),
html.H1('Netflix Movies and TV Shows Dashboard'),
html.Hr(),
dbc.Tabs(
[
dbc.Tab(label='地理内容分布', tab_id='tab1'),
dbc.Tab(label='内容分类', tab_id='tab2'),
],
id='tabs',
active_tab='tab1',
),
html.Div(id='tab-content', className='p-4'),
]
)
# 这个回调函数根据用户选择在仪表板中的选项卡之间切换。
# 它使用新选择的选项卡的布局来更新'tab-content'组件。
@app.callback(Output('tab-content', 'children'), [Input('tabs', 'active_tab')])
def switch_tab(at):
if at == 'tab1':
return geographical_content.layout
elif at == 'tab2':
return content_classification.layout
if __name__ == '__main__':
app.run(debug=True)
回调装饰器@app.callback
监听’tabs’组件的’active_tab’属性的变化,该组件由Input对象表示。
每当’active_tab’发生变化时,将触发switch_tab函数。该函数检查’active_tab’ id,并返回要在’tab-content’ Div中呈现的相应布局,如Output对象所示。因此,当您切换选项卡时,相关的可视化内容将出现。
最后,再次运行app.py以查看带有新可视化的更新仪表板:
总结
本文教您如何创建一个仪表板,探索和可视化Netflix的内容分布和分类。通过利用Python和Dash的强大功能,您现在可以创建自己的可视化,为您的数据提供宝贵的见解。
您可以在以下GitHub存储库中查看此项目的完整代码:https://github.com/gutyoh/netflix-dashboard
如果你发现这篇文章对你有帮助,并且想要扩展你对Python和数据科学的知识,考虑查看Hyperskill上的数据科学入门。
如果你对这篇博客有任何问题或反馈,请在下面的评论中告诉我。
Hermann Rösch 是Hyperskill上Go编程课程的技术作者,他将他对EdTech的热情与赋予下一代软件工程师的能力相结合。同时,他还在伊利诺伊大学厄巴纳-香槟分校攻读硕士学位,深入探索数据世界。
原文,经授权转载。