跳至主要内容

Altair Python 教程:数据可视化

学习如何使用 Altair 和电影数据集在 Python 中创建数据可视化。
· 12分钟 阅读

使用 AI 探索

使用 DataCamp 打造 Python 职业技能

能够创建既易于理解又足够精细的图表,是成为优秀数据科学家的关键。通过可视化,您可以讲述数据背后的故事。它能突出数据中的关系,并展现仅用数字无法传达、但人眼可见的信息。但您知道比静态可视化更适合数据分析的是什么吗?交互式可视化!

不幸的是,对于新手来说,这看起来可能是件艰巨的任务。Python 和 R 都提供了大量工具和技巧来帮助您完成这项工作。在本教程中,我们将向您介绍 Altair。借助 Altair,您只需几行代码、用很短的时间,就能创建有意义、优雅且高效的可视化。那就开始吧!

什么是 Altair?

Altair 是一个用于统计可视化的 Python 库。它具有声明式特性(我们稍后会解释这个概念)。它基于 Vega 和 Vega-Lite,这两者都是可视化语法(grammar of graphics),允许您用 JSON 格式描述可视化的外观和交互行为。

命令式 vs. 声明式 API

如前所述,Python 已经有一系列用于数据可视化的优秀工具和库,其中许多还支持交互式可视化。那么 Altair 的独特之处在哪里?

答案在于它相对于其他命令式 API 的声明式特性。

在命令式 API 中,您需要把注意力放在“如何做”而不是“想要做什么”上。您必须更多地考虑生成可视化所需的代码,并手动指定绘图步骤——坐标轴范围、大小、旋转、图例等。

作为数据科学家,Altair 能让您将时间和精力更多地放在数据本身——理解、分析和可视化——而不是实现这些所需的代码上。这意味着您可以定义数据以及期望看到的输出(最终可视化应呈现的样子),Altair 会自动为您完成必要的处理。

安装

在本教程中,我们将使用 Vega datasets 中的示例数据集。要安装 Altair 以及 Vega 数据集,请在控制台中输入以下命令:

$ pip install altair vega_datasets

如果您使用的是 conda 包管理器,对应命令为:

$ conda install -c conda-forge altair vega_datasets

之后,您就可以在 Jupyter Notebook 中导入 Altair 开始使用。若想进一步了解 Jupyter Notebook,请务必查看 DataCamp 的Jupyter Notebook 终极指南。

数据

现在我们已经安装了 Altair 和 Vega 数据集,是时候加载一些数据了。先从以下导入开始:

import pandas as pd
import altair as alt

为什么要导入 Pandas?

因为 Altair 的数据是围绕 Pandas DataFrame 构建的,这意味着您可以像处理 Pandas 的 DataFrame 一样在 Altair 中操作数据。

尽管 Altair 在内部以 Pandas DataFrame 的格式存储数据,但有多种方式可以输入数据:

  • 作为 Pandas DataFrame
  • 作为 Data 或相关对象(如 UrlData、InlineData、NamedData)
  • 作为指向 JSON 或 CSV 文本文件的 URL 字符串
  • 作为支持 geo_interface 的对象(例如,Geopandas 的 GeoDataFrame、Shapely 几何对象、GeoJSON 对象)

电影数据集

本教程将使用 Vega 数据集中的电影数据集(Movies Dataset)。这一类与电影相关的数据集在数据科学社区中很有名,尤其是在推荐引擎的语境下。不过,我们将在这里仅通过 Altair 的可视化来进行数据分析。

该数据集包含与电影相关的信息,例如片名、在美国和全球的票房、制作成本、类型、IMDB 和 Rotten Tomatoes 的评分等。我们不会使用所有列,而是只用其中一部分,来学习 Altair 作为可视化工具的用法。

让我们继续,获取数据集并做一些简单分析,以便更好地理解它。

# Importing the Vega Dataset
from vega_datasets import data as vega_data

movies_df = pd.read_json(vega_data.movies.url)

# Checking the type of data that we get
print("movies_df is of the type: ", type(movies_df))

print("movies_df: ", movies_df.shape)
movies_df is of the type:  <class 'pandas.core.frame.DataFrame'>
movies_df:  (3201, 16)

如您所见,movies_df 实际上是一个 pandas 的 DataFrame,其中包含 3201 行(电影)和 16 个特征列。让我们看看数据框中的内容。

movies_df.head(5)
 TitleUS_GrossWorldwide_GrossUS_DVD_SalesProduction_BudgetRelease_DateMPAA_RatingRunning_Time_minDistributorSourceMajor_GenreCreative_TypeDirectorRotten_Tomatoes_RatingIMDB_RatingIMDB_Votes
0The Land Girls146083.0146083.0NaN8000000.0Jun 12 1998RNaNGramercyNoneNoneNoneNoneNaN6.11071.0
1First Love, Last Rites10876.010876.0NaN300000.0Aug 07 1998RNaNStrandNoneDramaNoneNoneNaN6.9207.0
2I Married a Strange Person203134.0203134.0NaN250000.0Aug 28 1998NoneNaNLionsgateNoneComedyNoneNoneNaN6.8865.0
3Let's Talk About Sex373615.0373615.0NaN300000.0Sep 11 1998NoneNaNFine LineNoneComedyNoneNone13.0NaNNaN
4Slam1009819.01087521.0NaN1000000.0Oct 09 1998RNaNTrimarkOriginal ScreenplayDramaContemporary FictionNone62.03.4165.0

列名为:

movies_df.columns
Index(['Title', 'US_Gross', 'Worldwide_Gross', 'US_DVD_Sales',
       'Production_Budget', 'Release_Date', 'MPAA_Rating', 'Running_Time_min',
       'Distributor', 'Source', 'Major_Genre', 'Creative_Type', 'Director',
       'Rotten_Tomatoes_Rating', 'IMDB_Rating', 'IMDB_Votes'],
      dtype='object')

接下来我们将使用 pandas 的 to_datetime 函数把 Release_Date 列转换为真正的日期,然后从中提取年份。稍后我们会在可视化中使用这个新创建的列。

def extract_year(value):
    return pd.to_datetime(value, format='%b %d %Y').year

movies_df["Year"] = movies_df["Release_Date"].apply(extract_year)

现在如果您再次查看列名,会看到新增了一个 Year 列,其中包含对应电影的年份。

movies_df.columns
Index(['Title', 'US_Gross', 'Worldwide_Gross', 'US_DVD_Sales',
       'Production_Budget', 'Release_Date', 'MPAA_Rating', 'Running_Time_min',
       'Distributor', 'Source', 'Major_Genre', 'Creative_Type', 'Director',
       'Rotten_Tomatoes_Rating', 'IMDB_Rating', 'IMDB_Votes', 'Year'],
      dtype='object')

现在我们来创建一些图表。但在此之前,请先运行这段代码:

alt.renderers.enable('notebook')
RendererRegistry.enable('notebook')

上述代码让 Altair 能够在 Jupyter Notebook 中内联渲染,这样我们就能看到绘制的图形。如果遇到问题,请查看 Altair 的故障排查页面。

初步浏览数据后,看看电影制作投入(Production_Budget)与电影实际票房(Worldwide_Gross)之间的关系会很有趣。我们将分别把它们绘制在 X 轴和 Y 轴上。

不过,这个数据集相对较大——考虑到我们主要目的是练习 Altair,因此暂时只用一小部分数据。让我们先看看数据集中某一年的电影数量,并选择一个年份来聚焦。

movies_df["Year"].value_counts()
2006    220
2005    210
2002    208
2004    192
2000    188
       ...
1943      1
2015      1
2027      1
2037      1
1928      1
Name: Year, Length: 91, dtype: int64

我们把焦点放在 2000 年,并创建一个新的数据框 movies_2000,只包含该年的数据。它应包含 188 行(电影)。

movies_2000 = movies_df[movies_df["Year"] == 2000]
movies_2000.shape
(188, 17)

现在我们来制作第一个可视化。为此,先更深入了解一下 Altair 的格式。

图表(Charts)

Chart 是 Altair 的基本对象,它只接受一个参数——dataframe。定义图表的方式如下:

alt.Chart(DataFrame)

但图表本身除了指定要使用的数据外,不会做太多事情;接下来您需要告诉它要对数据做什么。这时就要用到 marks。

标记(Marks)

mark 属性用于指定数据在图中如何呈现。Altair 提供了以下基本标记属性:

对于本例中的 mark 属性,我们希望把 DataFrame 中的每个值都标记为图中的一个点(圆)。因此,我们将这样写:

alt.Chart(DataFrame).mark_point()

编码(Encodings)

在确定了数据以及呈现方式之后,我们还需要指定呈现的位置,也就是设置位置、大小、颜色等。这就要使用 encodings。因此,我们有:

alt.Chart(DataFrame).mark_point().encode()

把这些放在一起,并补充细节,来完成我们的第一个可视化:

alt.Chart(movies_2000).mark_point().encode(
    alt.X('Production_Budget'),
    alt.Y('Worldwide_Gross')
)
<vega.vegalite.VegaLite at 0x11709a1d0>
可视化 1

是不是很棒?我们几乎没做什么——只是熟悉了一些术语并把它们组合起来。现在我们有了一张图,可以看到 2000 年电影的投入与票房之间的关系。

让我们把这张图做得更有趣些。与其让所有点的圆大小一样,不如让它们的大小与对应的 US_Gross 成正比。为此,我们引入 Size 特性。圆越大,代表其在美国的票房越高。我们还将把圆填充上颜色,而不仅仅是描边;为此,我们在 mark_point() 中添加一个参数。

alt.Chart(movies_2000).mark_point(filled=True).encode(
    alt.X('Production_Budget'),
    alt.Y('Worldwide_Gross'),
    alt.Size('US_Gross')
)
<vega.vegalite.VegaLite at 0x117cce790>
可视化 2

现在,我们仅用四行代码就绘制了每部电影的预算与票房。还缺什么?颜色!

但我们不会为了加颜色而随便上色!我们将引入数据集中另一项信息 Major_Genre,让每种电影类型都有自己的颜色(使用 Color),并通过 OpacityValue 加一点透明度。

alt.Chart(movies_2000).mark_point(filled=True).encode(
    alt.X('Production_Budget'),
    alt.Y('Worldwide_Gross'),
    alt.Size('US_Gross'),
    alt.Color('Major_Genre'),
    alt.OpacityValue(0.7)
)
<vega.vegalite.VegaLite at 0x118e3ff50>
可视化 3

这已经不错了,但还可以更好!如果我们能在图上移动,并查看每个点所代表电影的详细信息就更棒了。Tooltip 正好能做到这一点……

alt.Chart(movies_2000).mark_point(filled=True).encode(
    alt.X('Production_Budget'),
    alt.Y('Worldwide_Gross'),
    alt.Size('US_Gross'),
    alt.Color('Major_Genre'),
    alt.OpacityValue(0.7),
    tooltip = [alt.Tooltip('Title'),
               alt.Tooltip('Production_Budget'),
               alt.Tooltip('Worldwide_Gross'),
               alt.Tooltip('US_Gross')
              ]
)
<vega.vegalite.VegaLite at 0x118e3ff90>
可视化 4

是不是很酷?不过先等等,我们还能做更多。还记得我们一开始为什么要用 Altair 吗?就是为了能够制作交互式可视化。tooltip 已经在悬停时带来了一些交互效果。不过,我们再加上一个简单的 interactive() 功能……

alt.Chart(movies_2000).mark_point(filled=True).encode(
    alt.X('Production_Budget'),
    alt.Y('Worldwide_Gross'),
    alt.Size('US_Gross'),
    alt.Color('Major_Genre'),
    alt.OpacityValue(0.7),
    tooltip = [alt.Tooltip('Title'),
               alt.Tooltip('Production_Budget'),
               alt.Tooltip('Worldwide_Gross'),
               alt.Tooltip('US_Gross')
              ]
).interactive()
<vega.vegalite.VegaLite at 0x118e3c750>
可视化 5

仅仅添加这一行代码,就可以在特定区域进行缩放——坐标轴范围会自动调整,便于您专注于感兴趣的区域。当然,还有更多高级的交互功能可以加入。我们来看看其中一个……

我们终于有了一张很酷的图来展示数据。现在把所有数据点都放到图里吧。不过等等,把所有数据点都堆在一张图中是否会太多?我们还希望按之前创建的 Year 值来区分。为此,我们将使用一个高级的 selection 属性。

select_year = alt.selection_single(
    name='Select', fields=['Year'], init={'Year': 1928},
    bind=alt.binding_range(min=1928, max=2046, step=10)
)

alt.Chart(movies_df).mark_point(filled=True).encode(
    alt.X('Production_Budget'),
    alt.Y('Worldwide_Gross'),
    alt.Size('US_Gross'),
    alt.Color('Major_Genre'),
    alt.OpacityValue(0.7),
    tooltip = [alt.Tooltip('Title:N'),
               alt.Tooltip('Production_Budget:Q'),
               alt.Tooltip('Worldwide_Gross:Q'),
               alt.Tooltip('US_Gross:Q')
              ]
).add_selection(select_year).transform_filter(select_year)
<vega.vegalite.VegaLite at 0x117cd4cd0>
可视化 6

我们使用 add_selection 将 select_year 作为单一选择功能引入图表,然后通过 transform_filter() 将该选择实际应用到图上。

您是否注意到代码中的另一个新增内容?Tooltip 中的 “Q” 和 “N”。它们用于指定数据类型:N-名义型(分类数据)、Q-定量型(数值数据)、O-有序型(有序数据)、T-时间型(时间点或时间区间)。

查看数据集中最早和最新的年份。

print("The oldest year is: ", movies_df["Year"].min())
print("The latest year is: ", movies_df["Year"].max())
The oldest year is:  1928
The latest year is:  2046

如您所见,当拖动“Select_Year”时,在 1928 — 1967 年之间没有太多电影;2018 年之后也没有记录,更不用说到 2046 年了。我们可以使用 binding_range() 来指定年份范围及步长。

在真实项目中,这类发现很重要,可能指向数据采集阶段或流程中某处存在错误。您需要有效地处理这些错误。这是数据预处理中的重要步骤。可参阅使用 pandas 进行数据准备以了解这些关键步骤的入门内容。

让我们调整年份范围,至此,我们的最终可视化已经就绪……

select_year = alt.selection_single(
    name='Select', fields=['Year'], init={'Year': 1968},
    bind=alt.binding_range(min=1968, max=2008, step=10)
)

alt.Chart(movies_df).mark_point(filled=True).encode(
    alt.X('Production_Budget'),
    alt.Y('Worldwide_Gross'),
    alt.Size('US_Gross'),
    alt.Color('Major_Genre'),
    alt.OpacityValue(0.7),
    tooltip = [alt.Tooltip('Title:N'),
               alt.Tooltip('Production_Budget:Q'),
               alt.Tooltip('Worldwide_Gross:Q'),
               alt.Tooltip('US_Gross:Q')
              ]
).add_selection(select_year).transform_filter(select_year)
<vega.vegalite.VegaLite at 0x1180cda10>
可视化 7

在本教程中,您初步了解了 Altair。您看到了如何用几行代码创建漂亮、可交互的图表,更有效地讲述数据中的故事。本文希望展示 Altair 的强大与潜力。想进一步了解 Altair,请访问其官方网站。

探索 DataCamp 的 Python 课程库横幅

主题