第 10 章 展示数据
你已经学习了如何分析数据,现在想要展示它。针对不同听众,演示可能会有很大的差别。我们会在这一章学习多种不同类型的演示:从简单的可以在电脑上制作的演示文件到交互式的网站。
取决于你想要展示的内容,通过图表、地图或图片进行可视化可能是你尝试讲的故事中一个重要的部分。我们会学习如何构建并运行自己的站点,分享发现。还会介绍如何分享 Jupyter notebook,其他人通过它可以看到你的代码、图表、图片和结论。
首先,我们会探索如何考虑你的听众,开始讲述你通过数据分析发现的故事。
10.1 避免讲故事陷阱
讲故事并不简单。取决于主题,你可能很难从数据中得出可靠的结论。你可能会遇到不一致或不确定的数据。这没关系。建议你继续研究,也许在数据集中找到的不同示例中就蕴含着故事。
讲故事时所面临的一些困难是由数据分析时的个人偏见带来的。正如经济学家和记者 Allison Schranger 在“The Problem with Data Journalism”(http://qz.com/189703/the-problem-with-data-journalism/)一文中讨论的,我们不可避免地会在分析时带有偏见。她的建议是,承认这些偏见,并试着去了解数据,避免为了讲故事的目的而去曲解它。
不要擅自假定你所要讲的故事和数据是一致的。尝试先研究数据,然后讲述数据研究所得。不要花太多时间来操作数据。如果需要大量修改数据(标准化、归一化和去除离群值),你可能应该找找其他故事或不同的数据。
记住一点,讲故事是成为领域专家的重要部分。通过研究数据掌握的信息有助于你阐明新的主题和观点。理解自己的偏见,带着这样谦逊的态度,你的故事将会有效且具有启发性。
10.1.1 怎样讲故事
确定你想要讲的故事,同确定如何去讲述它一样重要。你可以使用图表、图形、时间线、地图、视频、文字和交互式的内容来讲述故事。你可以在网上发布它,或者在各种会议中演示它。你可以将它上传到一个视频分享网站。无论选择什么方式,确保讲故事的方式增强了你的发现。没有什么比看到一个糟糕的展示更令人沮丧了,它实际上抹杀了你试图讲述的故事。
在下面几个小节中,我们会评估你的听众、你的故事和可用的平台是怎样影响演示选择的。建议你阅读所有这些选择,即使你已经有了有关展示发现方式的想法。这会让你更好地理解可用的选择,即使你坚持最初的选择。对于那些面对广泛听众的演示,不同形式的组合可能是最好的选择。
确定你计划未来多久更新一次数据是讲故事的另一个部分。是持续更新吗?你的听众不久之后就能听到更多关于这个故事的信息,还是要期待年度报告?你是否能够清楚地告诉他们何时及以何种方式更新?只有你清楚听众的期望,让他们等待才是一个不错的想法。
10.1.2 了解听众
听众和内容一样重要。通过识别目标听众,你可以确定他们关于某个话题已经知道什么,什么是他们最感兴趣的内容,以及对他们来说效果最好的学习方式是什么。如果未能与听众有效沟通,那么你创建的故事就会没人感兴趣。
如果报告或展示是你工作的一部分,确定听众应该十分容易。无论是工作中的一个小组、一个执行团队,还是一个日报或年刊,你精确地了解谁会阅读你的报告。
如果你想向更多人展示数据,你应该研究一下已有成果,以及哪些人想要进一步深入地了解。熟悉目标领域的已有成果会帮你确定现有或潜在的听众。
如果你不确定目标听众,一个好的策略是接触对该主题明显有不同兴趣程度的不同的人,例如,一位父辈或导师、一位同事和一名学员(从你的展示和话题的角度来看)。取决于有关话题的知识水平,是否不同的人对故事中的某一部分更感兴趣?是否不同年龄和经验的听众会提出不同的问题?在讲解某个话题时,注意听众的问题,同时观察他们的反应,并基于这些观察修正关于目标听众的说明。
一旦确定了目标听众,你可以更深入地了解他们。根据听众的不同,使用下面附注栏中的建议来帮助完善讲故事的方式。
与听众交谈
当思考如何讲述故事给听众时,搞明白他们如何学习和理解这个世界,特别是你的主题,这一点很重要。这些问题会指引你讲故事的过程,将发现以最佳方式传达给目标听众。
你的听众是如何学习新事物的?在线学习?口口相传?通过出版物?
关于这个主题你的听众有多少先验知识?是否有听众会感到陌生的单词或想法?
你的听众能否自己探索数据?
你的听众会花费多少时间和注意力听故事?
在与你或他人谈论这个故事时,听众的参与度如何?
如果有新的信息发布,你的听众是否想要被告知并更新信息?
这些只是众多问题中的一部分,你可以通过这些问题来确定真正的听众,以及他们如何才能最好地消化你的故事。以这些问题为最初的提示,让它们引导你找到更多关于如何分享发现的问题。
如果你找到了听众,准备好开始讲故事了,你就可以着手研究通过可视化工具讲述数据故事的方式了。
10.2 可视化数据
处理数据时,你可能希望使用一些可视化工具来讲故事。根据故事的不同,你的可视化选择可能是图表、图片或者时间线。无论你如何展示数据,第一步是确定哪些可视化数据是有用且相关的。
在以可视化方式讲故事的过程中,确定怎样展示发现至关重要。正像 Alberto Cairo 在其关于数据可视化的博客文章(http://www.thefunctionalart.com/2014/08/to-make-visualizations-that-are.html)中写道的,如果不展示所有相关的数据,可能会让听众对你的方法和发现有疑问。
这类似于用文档详细描述数据分析和方法论,我们需要文档化和保护可视化探索和数据的展现,确保不会遗漏故事中重要的部分。
在这一节中,我们会探索如何使用图表、时间序列、时间线、地图、混合多媒体、文字、图片和影音来分享发现。取决于你的听众,可能会有不同类型的工具适用于你的故事。每种类型都有其优势和缺点,我们会在探索的过程中介绍这些。
10.2.1 图表
图表是分享数字数据的良好方式,尤其是比较不同的数据集或不同的分组时。如果数据有一个清晰的趋势,或者数据显示出特定的离群值,图表会帮你向听众展现这些发现。
你可以使用条形图来并排展示大量数据。比如,在《华盛顿邮报》关于婴儿死亡率的报道(http://www.washingtonpost.com/blogs/wonkblog/wp/2014/09/29/our-infant-mortality-rate-is-a-national-embarrassment/)中,Christopher Ingraham 使用了条形图来并排比较不同国家的婴儿死亡率。
为了展示随时间推移的趋势,人们通常会使用折线图。Christopher Ingraham 同样使用了折线图来比较不同年龄下的婴儿死亡率。通过条形图可以看到,美国在婴儿保护方面滞后于其他国家。折线图可以比较不同国家的婴儿死亡率随时间变化的情况,让我们从另外一个角度观察数据。
你会注意到,作者选择在折线图上仅展示少数几个国家,而不是像在条形图上展示所有的国家。他为什么做出这样的决定?可能是他检查了数据,发现包含更多国家的数据会使得图表难以阅读。
这些都是可视化发现时需要做出的各种决定。为了确定什么样的图表适合你,什么类型的图表最有用,首先定义你想要通过图表展示什么。Extreme Presentation 博客(http://extremepresentation.typepad.com/blog/2006/09/choosing_a_good.html)上有简便的流程图工具,当你开始思考这些问题时,可以从这里开始。Juice Labs 开发了一个交互式图表选择器(http://labs.juiceanalytics.com/chartchooser),展示了一些相同的概念。
每个图表都有其优势和弱点。如果你想要展示关系,可以使用散点图、气泡图或者折线图,所有这些都可以展示数据相关性。条形图更适合比较多个对象。如果你想要展示数据的组成或因素,可以创建一个堆叠条形图。为了展示分布,你可以使用时序图或者柱状图。
让我们考虑一下迄今为止研究过的数据,使用一些 agate
内置特性来绘制数据。
- 使用
matplotlib
绘制图表
Python 的一个主要图表和图片库是 matplotlib
,可以用来绘制数据集。它是生成简单图表的很好的方式,你越熟悉图表库,你的图片和图表会越高级。首先,需要运行 pip install matplotlib
来安装它。
让我们展示政府腐败感得分和童工雇用率的对比,下面是代码。
import matplotlib.pyplot as plt
plt.plot(africa_cpi_cl.columns['CPI 2013 Score'],
africa_cpi_cl.columns['Total (%)']) ➊
plt.xlabel('CPI Score - 2013') ➋
plt.ylabel('Child Labor Percentage')
plt.title('CPI & Child Labor Correlation') ➌
plt.show() ➍
❶ 使用 pylab
的 plot
方法,传递 x 和 y 的标签数据。传递的第一个变量是 x 坐标系,第二个变量是 y 坐标系。这会创建一个 Python 图表,绘制这两个数据集。
❷ 调用 xlabel
和 ylabel
方法标记图表坐标系。
❸ 调用 title
方法为图表命名。
❹ 调用 show
方法来绘制图表。所有在调用 show
之前关于图表的操作,会显示在系统默认的图片程序中(例如 Preview 或 Windows 图片查看器)。标题、坐标标签和任何其他通过 matplotlib
设置的属性都会展示在这个图表中。
喔! Python 渲染了图 10-1 中的图表。1
图 10-1:童工和 CPI 图表
我们能够明确看到趋势是整体下跌的,但是同样看到中部的数据并没有呈现特定的趋势。事实上,数据变化得很剧烈,这说明童工和政府腐败感并不是在所有国家都有联系,只在部分国家有联系。
让我们仅使用情况最糟糕的国家的数据来绘制图表。在 9.2.1 节中,我们已经分离出了这些国家。使用 highest_cpi_cl
表再次运行前面的代码,会看到图 10-2 中的图表。
现在可以看到一个清晰的下降趋势,在这些最糟糕的国家中,随着童工雇用率和政府腐败感指数的下降,出现了一些异常。
图 10-2:高童工雇用率国家图表
pylab
中有很多种图表可用,包括直方图、散点图、条形图和饼形图。强烈建议你查看一下 matplotlib.org 关于 pyplot
的介绍(http://matplotlib.org/1.4.2/users/pyplot_tutorial.html#pyplot-tutorial),包括如何改变图表的不同属性(颜色、标签、大小),使用多图、子区(subplot)和更多图表类型。
图表化数据便于发现数据集中的异常或离群值。使用 Python 图表库中各种可用的图表方法,可以帮你研究数据中的故事和关系。
越多地使用库的图表工具集,理解哪个图表最适合你的数据集就越容易。
- 使用Bokeh绘图
Bokeh(http://bokeh.pydata.org/)是一个 Python 绘图库,能够用相当简单的命令来绘制更复杂的图表类型。如果想要创建一个条形图、散点图或时间序列图,尝试 Bokeh,看看是否合适。让我们尝试使用 Bokeh,基于各国家创建一个 CPI 和童工数据的散点图。运行下面的命令安装 Bokeh。
pip install bokeh
之后利用 agate
表使用一些简单的命令创建散点图:
from bokeh.plotting import figure, show, output_file
def scatter_point(chart, x, y, marker_type): ➊
chart.scatter(x, y, marker=marker_type, line_color="#6666ee",
fill_color="#ee6666", fill_alpha=0.7, size=10) ➋
chart = figure(title="Perceived Corruption and Child Labor in Africa") ➌
output_file("scatter_plot.html") ➍
for row in africa_cpi_cl.rows:
scatter_point(chart, float(row['CPI 2013 Score']),
float(row['Total (%)']), 'circle') ➎
show(chart) ➏
❶ 定义一个函数,scatter_point
,接受一个图表、x 轴和 y 轴值、标记的类型(圆形、正方形、矩形),并且添加这些点到图表中。
❷ 图表的 scatter
方法需要两个必需的参数(x 轴和 y 轴)和一些不同的关键参数,为这些点添加样式(包括颜色、透明度、大小)。这行代码传递了边缘颜色和填充颜色以及大小和透明度到函数中。
❸ 使用函数 figure
创建图表,同时传入一个标题。
❹ 使用函数 output_file
定义输出的文件。这会在你运行代码的文件夹下创建文件 scatter_ plot.html。
❺ 对于每一行数据,使用 CPI 得分作为 x 轴,童工雇用率作为 y 轴,添加一个数据点。
❻ 在浏览器窗口中展示这张图表。
当你运行这段代码,它会在浏览器中打开一个窗口,展示这张图表(见图 10-3)。
图 10-3:CPI 和童工散点图
这张图非常好,但是我们看不出这些点的含义。Bokeh 可以添加交互元素到图表中。让我们添加一些试试。
from bokeh.plotting import ColumnDataSource, figure, show, output_file
from bokeh.models import HoverTool ➊
TOOLS = "pan,reset,hover" ➋
def scatter_point(chart, x, y, source, marker_type): ➌
chart.scatter(x, y, source=source,
marker=marker_type, line_color="#6666ee",
fill_color="#ee6666", fill_alpha=0.7, size=10)
chart = figure(title="Perceived Corruption and Child Labor in Africa",
tools=TOOLS) ➍
output_file("scatter_int_plot.html")
for row in africa_cpi_cl.rows:
column_source = ColumnDataSource(
data={'country': [row['Country / Territory']]}) ➎
scatter_point(chart, float(row['CPI 2013 Score']),
float(row['Total (%)']), column_source, 'circle')
hover = chart.select(dict(type=HoverTool)) ➏
hover.tooltips = [
("Country", "@country"), ➐
("CPI Score", "$x"),
("Child Labor (%)", "$y"),
]
show(chart)
❶ 导入我们用过的主要的库,并导入 ColumnDataSource
和 HoverTool
类。
❷ 为最终的图表定义你想要使用的工具(http://bokeh.pydata.org/en/latest/docs/user_guide/tools.html#specifying-tools)。这行代码添加了 hover
,所以可以使用悬停方法。
❸ 把 source
添加到必需的参数中。这会存储国家名称信息。
❹ 传递 TOOLS
变量到图片初始化函数中。
❺ 变量 column_source
现在保存着一个数据源字典,其中是国家名称。这一行代码将国家名称作为列表传递,因为字典的值必须是一个可迭代对象。
❻ 从图表中选择 HoverTool
对象。
❼ 使用悬停对象的 tooltips
方法,展示不同的数据属性。@country
选择了通过列数据源传入的数据,而 $x
和 $y
选择图表中 x 和 y 数据点。
现在你的图表应该看起来类似于图 10-4。
图 10-4:CPI 和童工交互散点图
随着你移动游标到每一个点,x 和 y 的数据会随之变化。为了优化这个图表,可以通过输入新的键值对到
data
字典中,添加精确的数据点到column_source
对象中。
Bokeh 有一个很好的示例库(http://bokeh.pydata.org/en/latest/docs/gallery.html)和可用的代码来帮助你上手。建议你花些时间在图表上,尝试一下 Bokeh。
1如果图表没有展示出来,根据 Stack Overflow 上的指示(http://stackoverflow.com/questions/7534453/matplotlib-does-not-show-my-drawings-although-i-call-pyplot-show/7534680#7534680),来确定 matplotlib
设置在哪里,并设置库的后端为默认选项之一(Mac/Linux 系统上的 QT4Agg 或 Windows 系统上的 GTKAgg)。对于 Windows 系统,可能同样需要运行 pip install pygtk
。
10.2.2 时间相关数据
时间序列和时间线数据展示随时间推移的发现。时序图表展示数据随时间的变化(通常表现为折线图、柱状图或直方图)。时间线允许你通过标记随着时间推移发生的活动、事件和变化来直观地讲述数据的故事。
- 时间序列数据
时间序列展示随时间推移产生的趋势,尤其是当聚焦于一个因素时十分有效。《华尔街日报》发布过一个非常棒的关于疫苗和发病率的时间序列(http://graphics.wsj.com/infectious-diseases-and-vaccines/)。交互元素允许你进行探索,内置的延时移动画创造了一个易读的画面。疫苗引进标记也增加了阅读的清晰度。
我们还没有研究数据集随时间推移的变化。下一步操作最好是收集过去几年的数据集。这些数据可以回答类似于这样的问题:随时间推移什么地方的童工数量在增长?是否能够看到随时间推移的一个明显的地区趋势?当合并另一个数据集,是否能够看到其他的趋势(例如,童工雇用数量是否伴随农业出口增长)?
在 Stack Overflow(http://stackoverflow.com/questions/19079143/how-to-plot-time-series-in-python)上有一个很好的回答,提供了更多的关于使用 matplotlib
来绘制时间序列数据图表的信息。还记得在第 9 章中 agate
表的 rows
和 columns
方法可以用来选择一列或者一行的数据吗?这两个方法返回的列表可以被传入任何 matplotlib
函数,把数据传入到图表中。
如果你想要了解如何使用 Bokeh 处理时间相关数据,可以查看它们很棒的实例(http://bokeh.pydata.org/en/latest/docs/user_guide/charts.html#timeseries)。
- 时间线数据
时间线数据可以帮助你向听众介绍某主题历史中重要的时刻,或者最近发展中的转折。例如,疫苗历史网站(History of Vaccines,http://www.historyofvaccines.org/content/timelines/measles)中的时间线展示了加利福尼亚麻疹疫苗的历史和最近发展,这样听众可以通过历史数据快速地理解主题。
如果想要展示童工历史信息的时间线,我们会去查找全世界童工历史中重要的时刻。我们可以去研究帮助引出时间线上事件的问题,例如:第一部保护儿童安全的法律是什么时候实施的?什么时候公众意见开始反对童工雇用?有哪些与童工有关的公众事件和丑闻?
对于可视化数据来说,Knight 实验室的 TimelineJS(http://timeline.knightlab.com/)可以接受一个数据表单,创建一个简单的交互式时间线。
10.2.3 地图
如果你的发现聚焦于地理信息,地图是展示数据的很好的方式。地图能帮助人们意识到某主题对他们了解的人群和地区的影响。根据听众对所讨论地区的了解程度,你可能需要在地图中包含额外的信息和上下文,便于将故事与听众更熟悉的区域相关联。
如果是本地听众,你可能要提到本地知名的纪念物和街道名称。如果是国际听众,并且故事涉及一个特定的区域(例如,亚马逊森林砍伐),则首先引用大洲地图,之后再聚焦于你的目标区域。
地图是数据可视化中很难的一种形式。不仅你要了解听众的地理知识,而且地图并不总能用清晰易理解的方式展示趋势。当使用地图时,对展示区域的地理知识非常熟悉是很重要的,你需要展示重要的地理元素,让听众确定位置,同时展示发现。
一个有新闻价值的地图的实例是《纽约时报》的加州疫苗接种地图(http://www.nytimes.com/interactive/2015/02/06/us/california-measles-vaccines-map.html?_r=1)。该地图在加州最近的麻疹疫情爆发期间发布,读者可以放大和缩小来查看更多信息,且该地图提供了简短的描述,展示了个人意愿和其他造成低接种率的原因(例如贫穷或者缺少接触)之间的不同。通过仅仅聚焦于加利福尼亚,该地图能够展示足够详细的细节,而如果是以国家或者地区为维度的话,可能会太杂乱或者复杂了。
准备地图时,你可能想要利用 ColorBrewer(http://colorbrewer2.org/),这个工具可以并排比较不同的地图颜色方案。你希望颜色不仅能讲述故事,而且有对比性,这样读者可以清晰地看到组和组之间的区别。
一个更大地理区域的地图实例是《经济学人》杂志的全球债务钟(http://www.economist.com/content/global_debt_clock)。这个地图展示了各个国家的公共债务情况,使用了一个交互时间线来展示随时间推移公债的变化情况。其互补的颜色方案使得地图很容易阅读,人们可以很容易地分辨负债很重的国家和负债很少或者没有债务的国家。
全球债务钟地图(the global debt clock map)的作者标准化了债务度量,使用美元作为标准的展示单位,所以用户可以并排地比较不同国家的债务率。这一很小的标准化有助于听众理解并增强了这些发现的影响。
有一个很容易使用的图表和地图 Python 库,pygal
(http://pygal.org/),它拥有很好的内置地图特性。pygal
有从饼状图、散点图到世界和国家地图详细的文档。可以同时使用 pygal
和 agate
表来展示世界范围内的童工雇用率。首先需要通过运行下面的命令安装库和其依赖。
pip install pygal
pip install pygal_maps_world
pip install cssselect
pip install cairosvg
pip install tinycss
pip install lxml
在 pygal
世界地图文档(http://www.pygal.org/en/latest/documentation/types/maps/pygal_maps_world.html?highlight=world)中,可以看到每个国家的双字符 ISO 编码是使用世界地 图所必需的。使用之前的方法添加这些编码到 ranked
表:
import json
country_codes = json.loads(open('iso-2.json', 'rb').read()) ➊
country_dict = {}
for c in country_codes:
country_dict[c.get('name')] = c.get('alpha-2') ➋
def get_country_code(row):
return country_dict.get(row['Countries and areas']) ➌
ranked = ranked.compute([('country_code',
agate.Formula(text_type, get_country_code)), ])
for r in ranked.where(lambda x: x.get('country_code') is None).rows: ➍
print r['Countries and areas']
❶ 加载从 GitHub 用户 @lukes 仓库(https://github.com/lukes/ISO-3166-Countries-with-Regional-Codes)中下载的文件 iso-2.json 中的字符串。这一文件在本书仓库中也可找到。
❷ 创建国家字典,键是国家名称,值是 ISO 编码。
❸ 定义新的函数 get_country_code
,接受一行数据,使用 country_dict
返回国家编码。如果没有对应键,返回 None。
❹ 查看没有匹配到的数据,做进一步的研究。
你应该看到类似下面的输出。
Bolivia (Plurinational State of)
Cabo Verde
Democratic Republic of the Congo
Iran (Islamic Republic of)
Republic of Moldova
State of Palestine
The former Yugoslav Republic of Macedonia
United Republic of Tanzania
Venezuela (Bolivarian Republic of)
我们发现大多数数据均匹配,但是有几个漏掉的。如在前一章中处理 earth.json 一样,通过修改数据文件中不匹配国家的名称手动修正这个问题。清洗后的文件,iso-2-cleaned.json,同样可以在仓库中找到。现在可以用新的、清洗后的 JSON 文件同之前的代码一起创建一个完整的表。注意,你需要重命名列或者使用新的列名称 contry_code_complete
,这样不会出现重复列名称的问题。我们会利用这张表,使用 pygal
地图方法创建自己的世界地图。
import pygal
worldmap_chart = pygal.maps.world.World() ➊
worldmap_chart.title = 'Child Labor Worldwide'
cl_dict = {}
for r in ranked.rows:
cl_dict[r.get('country_code_complete').lower()] = r.get('Total (%)') ➋
worldmap_chart.add('Total Child Labor (%)', cl_dict) ➌
worldmap_chart.render() ➍
❶ pygal
库中 map.world
模块的 World
类返回地图对象。
❷ cl_dict
保存着一个字典,键是国家编码,值是童工百分比。
❸ 根据 pygal
的文档,这行代码传递数据的标签和数据字典到函数中。
❹ 调用地图的 render
方法来展示地图。
可以看到 render
将 .svg 文件以一个很长很复杂的字符串输出到终端。如果想要保存它到一个文件,需要调用一个不同的方法。pygal
提供了几个选项,对应不同的文件类型:
worldmap_chart.render_to_file('world_map.svg')
worldmap_chart.render_to_png('world_map.png')
如果在渲染地图时有任何的问题,确保所有的依赖库已经安装。如果电脑上没有 .svg 文件查看器,你可以在浏览器中打开 .svg 文件。
强烈建议你查看除 .svg 之外 pygal
提供的其他选项。文档中有很多实例,高级的和简单的都有,对初学者来说,它也是一个很容易使用的 .svg 库。
10.2.4 交互式元素
交互式元素通过网站交互或模拟讲故事。因为用户可以通过浏览器四处点击和探索,所以他们可以以自己的节奏了解这个主题,从数据中得出自己的结论。这对于需要更加深入研究才能充分理解的主题特别有用。
作为对最近在美国麻疹疫情爆发的回应,英国《卫报》制作了一个疫情爆发交互式网页(https://www.theguardian.com/society/ng-interactive/2015/feb/05/-sp-watch-how-measles-outbreak-spreads-when-kids-get-vaccinated),允许用户查看和回放拥有不同疫苗接种率的各地区爆发潜在麻疹疫情的影响。这个交互式网页展示了《卫报》工作人员研究和编码的不同场景。不是每一种模拟都得到相同的输出,用户可以看到有一个展示接种率的控件,并且同时展示了患病概率(即,更高的疫苗接种率会有更小的感染可能)。这个网页使用了一个高度政治化的主题,并且使用疫情爆发的统计学模型呈现出了真实世界的场景。
尽管交互式元素需要更多的经验来创建,并且经常需要更高的编码技能,但它们是非常棒的工具,特别是当你有前端编码经验的时候。
举个例子,对于童工数据,可以创建一个交互式页面,用于展示在乍得本地高中,有多少人由于童工雇用率而可能永远不会毕业。另一个交互式页面可以展示当地商场中利用童工生产或提供的商品和服务。交互式元素将难以可视化的信息展示给受众,这样他们可以理解数据,与你的故事建立联系。
10.2.5 文字
通过文字讲故事,对作者和记者来说是非常自然的。无论你使用哪一种视觉方式,使用的所有文字对目标听众应该有用且合适。你可能想要采访该领域的专家,或同他们交谈。引用他们关于该发现的语言、想法和结论,会帮助听众综合这些信息。
如果正在研究一个本地的学校董事会是如何确定下一个学年的预算的,你可以同董事会成员谈话,或许还能得到有关提议修改的内部信息。如果正在研究公司准备发布的新产品,你可能想要同一些关键决策者谈话,确定可能会有什么新产品。
如果想获得更多关于访谈以及如何使用引言来完善故事的内容,Poynter 针对如何成为一名更好的访谈者提出了一些很棒的建议(http://www.poynter.org/2013/how-journalists-can-become-better-interviewers/205518/),哥伦比亚大学的访谈准则(http://www.columbia.edu/itc/journalism/isaacs/edit/MencherIntv1.html)分享了一些关于如何准备访谈以及如何根据项目需要准备不同的访谈的建议。
如果你是一个领域的专家,使用了一些技术或不熟悉的术语,依照听众的情况,你可能需要将这些主题分解为一个个小块。一个简单的术语表会很有用。对于科学、技术和医疗著作,当它们面向的是更广泛的读者时,使用术语表是非常普遍的做法。
10.2.6 图片、视频和插画
如果你的故事有一个很强的视觉元素,图片和视频可以增强这个故事。例如,与主题相关人物的视频访谈可以展示数据的个人立场,并且可能揭开了研究中其他的视角或未来的方向。
和视频一样,图像可以为观众描绘画面。根据我们的经验,使用图形图像来描绘战争或其他可怕的事情,总会影响我们对故事的解释。但是,使用图片来简单地震撼观众会导致他们忽略你为工作所做的细致研究。思考一下这一点,为你的故事找到一个平衡点。
如果你无法获得与主题相关的照片或者没有能力自己收集,可以用插画讲故事。《华盛顿邮报》一篇关于健康与不健康办公作空间的报道(http://www.washingtonpost.com/wp-srv/special/health/unhealthy-vs-healthy-office/),使用了一张插画来展示故事的概念。
对于童工数据来说,我们不大可能有机会自己收集在数据分析中发现的罪行的视频和照片。然而,我们可以使用过去关于童工报道的图片(在经过允许并说明来源的情况下),来揭示儿童仍然遭受着这个世界性问题的影响。
10.3 展示工具
如果你不想发布数据,但是想将其展示给一小群人(或内部人员),创建一个幻灯片相对更简单一些。在展示数据有很多方式可选时,你可以创建一个灵活的幻灯片,而不需要做太多额外的工作。
一个拥有很高评价的幻灯片制作工具是 Prezi(https://prezi.com/),它可以创建看起来很专业的幻灯片。Prezi 让你能够创建公共可用的幻灯片,而且它有多种不同的桌面客户端(如果你想要创建私密幻灯片,需要注册一个付费账户)。Haiku Deck(https://www.haikudeck.com/)是另一个只可在线使用的网站工具,可以免费制作公开的幻灯片,付费制作私密的幻灯片。你还可以将 Google Slides 作为一个免费且简单的选择,特别是当你准备给内部人员演示并且你的公司也使用 Google Apps 的时候。
10.4 发布数据
你已经花费了时间研究、探索和展示数据,现在想要通过网络分享报告给全世界。当你准备在网络上发布数据时,首先应该确定数据是否能够被公开访问。
如果你的展示包含私密的数据或者数据只与你的公司相关(专利数据),你应该在一个受密码保护的网站上发布它,或者在内网发布。
如果你想要和世界分享数据,通过诸多网络平台之一来发布不会有问题。在这一节里,我们会介绍如何在易于使用的免费博客平台或者你自己的网站上发布数据。
10.4.1 使用可用站点
许多网站被设计用来发布数据,以迎合类似你这样的想要分享报告或想法并将它们简单地发布到网络上的作者和研究者。下面是一些较好的选择。
- Medium
在 Medium(https://medium.com/)上,你可以创建一个账户,开始写自己的文章,轻松地嵌入评论、引用、图片和图表。因为这是一个社交媒体平台,所以其他的 Medium 用户可以推荐你的文章,分享它,标记它,同时关注你之后的文章。
使用一个类似 Medium 的托管站点让你能够专注于写作和报告,不用花时间去琢磨如何搭建和维护你自己的网站。
Medium 团队维护了一些很好的图表工具,包括 Charted.co(https://github.com/mikesall/charted),它使用简单的 CSV 或 TSV 文件来渲染一个交互图表。在写作本书时,它们还没有实现直接嵌入这些图表到文章中的能力,但是很有可能会添加这个功能。
Medium 使得将不同种类的社交媒体、视频、照片和其他媒介嵌入文章很容易(https://medium.com/@Medium/embed-videos-tweets-music-and-more-into-your-medium-stories-3b5c09c116e8#.w5e3j4n9v)。你可以通过阅读 Medium 每月最受欢迎的文章(https://medium.com/top-100/)来获得很多很棒的关于讲故事的想法。
建议阅读和搜索你主题领域的 Medium 博文,同这一主题的其他作者联系,来了解他们是如何讲故事的。
Medium 是在社交网络上写作并将你的想法分享给世界的很好方式。但是如果你想要运行自己的博客呢?继续阅读下面关于搭建和运行网站的选择。
- 快速上手的网站:WordPress、Squarespace
如果想要对布局和内容有更多的控制,你可以在 Squarespace(http://www.squarespace.com/)或 WordPress(https://wordpress.com/)上搭建自己的博客。这两个平台给了你一个免费(WordPress)或者廉价(Squarespace)的被维护的站点,让你可以自定义站点的外观和风格。你可以设置一个域名,这样你的文章会挂载在自己的 URL 下。
大多数虚拟主机提供商为 WordPress 开发了一键安装版本供你使用。你需要选择一个用户名和一些站点标题,并且确保有一个足够强大和安全的密码。在 WordPress 里,你有很多主题(https://wordpress.org/themes/browse/popular/)和插件(https://wordpress.org/plugins/browse/popular/)可以选择,来自定义网站的样式、风格和功能。为了保护站点,建议你安装一个流行的安全插件,并阅读 WordPress 关于安全的建议(http://codex.wordpress.org/Hardening_WordPress)。
使用 Squarespace,只需要注册一个账户,选择一个布局。你可以自定义相关联的社交媒体、域名,以及你是否想要有一个电商店铺。
一旦站点就绪,并且运行起来,添加内容是非常简单的。你会想要发布新的页面或文章,使用内置的编辑器添加文字和图片(或者,如果你正在使用 WordPress,可以安装额外的编辑器插件,支持更多的特性),之后发布内容。
你可以让文章更容易被找到,方法是通过 SEO 花费一些时间来填充描述和关键词,来提高文章的可见度。WordPress 插件和 Squarespace 特性可以为每篇文章做到这些。
- 自有博客
如果你运行着自己的网站或博客,你已经有了一个很棒的分享报告的平台。你需要确保可以适当地将视觉故事嵌入到网站中。生成的大多数图表可以很容易地嵌入到网站的 HTML 代码中。
如果你在使用除了 WordPress 或 Squarespace 以外的其他平台,可能需要研究在站点上如何分享图表、视频和照片。建议你联系平台的社区或创建者,或者阅读站点的指引和文档,来确定如何最好地嵌入图片、图表和交互式元素。
10.4.2 开源平台:创建一个新网站
我们已经提到了几个使用 Squarespace 和 WordPress 等免费或廉价平台创建和运行新站点的选项;但是如果你想要启动、运行和维护自己的站点,可以从众多伟大的开源平台中选择一个。
- Ghost
Ghost 是一个很容易运行的平台(https://github.com/tryghost/Ghost)。Ghost 使用 Node.js(https://nodejs.org/),一个开源的 JavaScript 异步服务器;如果你对 JavaScript 很感兴趣,使用和学习它都很有趣。因为它是异步的,所以拥有很好的性能,能够处理大量的请求。 Ghost 还提供了搭建托管站点的能力(https://ghost.org/),与 WordPress 或 Squarespace 类似,收取一定的费用。
如果你想要挂载自己的 Ghost 博客,DigitalOcean 和 Ghost 合作提供了一个容易使用和安装的服务器镜像(https://www.digitalocean.com/community/tutorials/how-to-use-the-digitalocean-ghost-application),不用一个小时就可在你的服务器上创建和运行 Ghost 站点。如果这是你第一次搭建服务器,强烈建议使用这个方式,因为一些初始的工作已经替你完成。
如果你有自己的服务器,并且想要在其他的平台上从头开始安装 Ghost,Ghost 提供了一些教程(http://support.ghost.org/deploying-ghost/)。你需要执行以下主要步骤。
(1) 下载并安装最新的源代码。
(2)运行 node
[建议你使用 nvm(https://github.com/creationix/nvm)]。
(3)使用 npm
(node 版本的 pip)安装 node
依赖。
(4)运行 pm2
(https://github.com/Unitech/pm2)来管理 Ghost 进程。
(5) 启动 nginx,使用网关与运行的 Ghost 进程通信。
(6) 开始写博客!
如果你遇到了问题,可以登入 Ghost 的 slack 频道(https://ghost.org/slack/),看看是否有人能够帮助你,或者在 Stack Overflow(http://stackoverflow.com/)上搜索更多相关的信息。
- GitHub Pages和Jekyll
如果你使用 GitHub 托管代码,你也可以用它来托管自己的网站。GitHub Pages(https://pages.github.com/)是一个依靠 GitHub 运行的网站托管工具,让你可以灵活地部署,并能轻松创建内容。使用 GitHub Pages,你可以通过将静态内容提交到你的仓库,直接部署你的 GitHub 页面。如果你喜欢使用框架,可以使用 Jekyll(http://jekyllrb.com/),一个基于 Ruby 的静态页面生成器,它集成了 GitHub Pages。
Jekyll 的文档(http://jekyllrb.com/docshome)有一个说明性的概述,涵盖了如何在本地安装和运行 Jekyll,但是建议你阅读 Barry Clark 为 Smashing 杂志编写的文章(https://www.smashingmagazine.com/2014/08/build-blog-jekyll-github-pages/)。在这篇文章中,他阐述了如何复制(fork)一个已有仓库,运行自己的站点,以及修改 Jekyll 的设置和特性。如果你不想使用 Jekyll,但是仍然想使用 GitHub Pages,你可以使用库或者手动地生成静态的 HTML 文件,再将这些文件提交到 GitHub Pages 仓库。
一个很容易使用的 Python HTML 生成器是 Pelican(https://github.com/getpelican/pelican),它可以接受 AsciiDoc、Markdown 或者 reStructuredText 格式的文件,并将它们转化为静态内容。它提供了简单的步骤来启动评论和访问分析,以及使用 GitHub Pages 的相当全面的介绍(http://docs.getpelican.com/en/latest/tips.html#publishing-to-github)。
还有很多其他的静态站点生成器,也还有很多关于如何将它们与 GitHub Pages 集成的文章。一个搭建 GitHub Pages 博客的选择是 Hexo(http://jdpaton.github.io/2012/11/05/setup-hexo/),它是一个基于 Node.js 的框架。Octopress(https://github.com/octopress/octopress)是另外一个很棒的选项,它基于 Jekyll 构建,所以你可以轻松地使用 GitHub Pages 和 Ruby 来发布和部署站点。
- 一键部署
如果你坚持使用大型的博客工具或网站框架,比如 WordPress,DigitalOcean 有很多一键安装的包(https://www.digitalocean.com/features/one-click-apps/),让你能够在短时间内搭建自己的服务器,并安装所有必需的库和数据库。它同样提供了便捷的入门指引,描述了如何在 droplet2)上安装 WordPress(https://www.digitalocean.com/community/tutorials/how-to-use-the-wordpress-one-click-install-on-digitalocean)。
除了大型的虚拟主机提供者外,你同样可以在 Heroku(一个基于云的应用主机服务商,https://devcenter.heroku.com/start)上使用 Python、Ruby 和其他开源的平台。如果你正在使用或学习一个开源框架,可以使用 Heroku 来部署自己的网站;它提供了很棒的文档和技术支持。
无论你使用哪一个框架或解决方案,专注于用简单的方式在网络上发布内容或代码,是很重要的。选择一些简单直接的方案,并专注于恰当地向全世界展示、发布和分享内容。
2DigitalOcean 虚拟主机的别名。——译者注
10.4.3 Jupyter(曾名IPython notebook)
我们已经介绍了怎样分享你的发现,但是如果你还想分享代码、数据或者研究过程呢?根据听众的不同,分享代码并允许人们直接与其交互可能是很合适的。如果你准备分享给同事和同行,这是一个很好的展示你如何进行研究的方式。
Jupyter notebook(https://jupyter.org/,曾名 IPython notebook,http://ipython.org/notebook.html)是一个很好的分享 Python 代码和代码生成的图表的方式。这些 notebook 组合了易于使用的浏览器和 IPython 的交互特性。notebook 在迭代代码设计和数据探索中也非常有用。
正在学习新的库或者使用新数据?在 Jupyter notebook 中保存你的工作。一旦完成了迭代并优化了代码,你可以将代码中重要的部分移动到仓库中,恰当地结构化、文档化,将这些东西综合在一起。
使 Jupyter 就绪并在本地运行它非常简单,只需运行这个命令:
pip install "ipython[notebook]"
要启动 notebook 服务器,运行:
ipython notebook
你看到的终端输出应该类似于:
[NotebookApp] Using MathJax from CDN: https://cdn.mathjax.org/mathjax/latest/MathJax.js
[NotebookApp] Terminals not available (error was No module named terminado)
[NotebookApp] Serving notebooks from local directory: homefoo/my-python
[NotebookApp] 0 active kernels
[NotebookApp] The IPython Notebook is running at: http://localhost:8888/
[NotebookApp] Use Control-C to stop this server and shut down all kernels.
Created new window in existing browser session.
这是 notebook 服务器启动的过程。你会看到一个新的浏览器窗口(或 tab)打开一个空的 notebook。
根据运行 notebook 文件夹的不同,你可能会在浏览器中看到一些文件。notebook 服务器直接在当前文件夹中运行,并展示这个文件夹的内容。建议为 notebook 创建一个新的文件夹。为了创建新的文件夹而停止服务器,需在运行的终端中输入 Ctrl-C(Windows 和 Linux 上)或者 Cmd-C(Mac 上)。创建一个新的目录,切换目录到这个文件夹下,重新启动服务器,类似下面这样:
mkdir notebooks
cd notebooks/
ipython notebook
让我们通过创建一个新的 notebook 来使用 Jupyter。为了达到这个目的,点击 New 下拉菜单,选择 Notebooks 头部下的 Python 2。创建好新的 notebook 后,给它一个有用的名称。为此,点击 title 区域(这里当前应该为“Untitled”),输入一个新名称。为 notebook 命名会在将来节省你大量的搜索时间。
在 Jupyter 中,每一个文本区域被叫作单元。notebook 支持多种不同的单元类型。在顶部和代码间使用一些 Markdown(https://daringfireball.net/projects/markdown/syntax)单元来解释并给代码添加文档是一个很好的想法。图 10-6 展示了一个添加头部(header)的示例。
图 10-5:添加 Markdown 标题
要开始编写 Python,只需点击下一个可用的单元,然后输入即可。当你完成了编写的语句或函数后,敲击 Shift+Enter。代码会执行并出现一个新的单元,在这里你可以编写下一个 Python 代码。正如在图 10-7 和你自己的 notebook 中看到的那样,你可以看到在一个普通的 Python 解释器中会看到的所有输出。
图 10-6:在 Jupyter 中工作
有很多非常棒的 Jupyter(和 IPython)notebook 指南,但是一个很好的入手点可能是重新尝试一些本书中使用过的代码。
建议组织你的 notebook,使其类似于你的仓库。你可能希望根目录下有一个包含数据的数据(data)文件夹,以及一个包含可导入 notebook 的脚本的工具(utils)文件夹。你的 notebook 就像另一个脚本,只是它是交互式的,并且在浏览器中。
用完 notebook,点击保存按钮(确保它创建一个新的检查点,这样可以更新你的文件)。
如果你在一个特定的 notebook 中完成了工作,但是仍在使用其他的 notebook,停止老的 notebook 进程是明智的选择。为此,选择服务器上的 Running 标签,并点击 Shutdown 按钮。当你对所有的 notebook 完成了编辑,保存它们并使用 Ctrl-C 或 Cmd-C 在运行 notebook 的终端停止服务器。
共享的Jupyter notebook
现在你已经熟悉 Jupyter notebook 的使用了,可以通过共享服务器上传并分享代码。这使得他人可通过普通网络(不仅是本地主机,例如前文在你的终端上运行的 notebook)访问你的 notebook。
有一些很棒的入门教程说明了如何使用 DigitalOcean(http://calebmadrigal.com/ipython-notebook-vps/)、Heroku(https://github.com/mietek/instant-ipython)、Amazon 网络服务(https://gist.github.com/iamatypeofwalrus/5183133)、Google DataLab(https://cloud.google.com/datalab/),或者你喜欢的任意服务器(http://ipython.org/ipython-doc/1/interactive/public_server.html#notebook-public-server),来搭建一个 notebook 服务器。
记得在 notebook 服务器上使用安全密码,保证 notebook 只被有这个密码的人使用。这会确保服务器和数据安全。
建议你也为 Jupyter notebook 建立一个类似 Git(第 14 章再深入探索)这样的版本控制系统,这样你就有了 notebook 每天或每周的历史记录。通过这种方式,你可以恢复删除的东西,同时帮你保存和组织代码。
如果你正在使用一个共享的 notebook 服务器,确保人们知道内核被中断(这在服务器重启或者有人终止或重启 notebook 内核时都会发生)时如何运行所有的代码。为了运行所有 notebook 代码,选择 notebook 工具栏中的 Cell 下拉菜单,然后点击 Run All 按钮。你也应该建议用户在完成工作后使用 Shutdown 终止 notebook,这样服务器上就没有无用的运行进程。
无论本地还是共享的 Jupyter notebook 都是展示数据和工作流的很好的工具。当你回顾数据探索和分析时,在本地运行它们将会非常有用。随着 Python 知识的增长,你可以将脚本迁移到 Python 3,同时运行 JupyterHub(https://github.com/jupyter/jupyterhub)。JupyterHub 就一个多用户的 notebook 服务器,运行着多种语言(包括 Python),当前正处在积极地开发中。
无论选择在 notebook 服务器还是开源平台上发布,你现在已经掌握了技能,能够分析如何以最佳方式展现和发布你的发现、数据和代码。
10.5 小结
你已经学习了如何将数据转化为可演示的形式,并且通过互联网传播它。你有很多发布选择,它们有不同的隐私等级和维护需求。你可以为报告搭建一个网站,并创建美丽的图片和图表来讲述故事。有了 Jupyter 的帮助,你可以很容易地分享和演示你写的代码,同时交他们一点 Python 知识。
你同样学习了表 10-1 中列出的库和概念。
表10-1:新的Python和编程概念和库
概念/库 | 功能 |
---|---|
用于绘图的 matplotlib 库
| 可以使用两个图表库生成简单的图表。你可以为图表使用标签和标题,用更清楚的方式展示数据 |
用于更复杂图表的 Bokeh 库 | 允许你轻松地生成更复杂的图表,可以在图表中添加交互式元素 |
用于 SVG 图形和地图的 pygal 库
|
pygal 让你能够使用简单的函数传递数据生成 SVG 图片
|
Ghost 博客平台 | 基于 Node.js 的博客平台,让你能够在自己的服务器(或挂载在 Ghost 上的平台)上快速地构建一个博客,在自己的网站上分享故事 |
GitHub Pages 和 Jekyll | 一个集成在 GitHub 上的简单发布平台,你可以通过简单地提交代码到仓库中来分享文章和演示文档 |
Jupyter notebook | 一个同其他开发者或同事分享代码的简单方式,同样也是一种使用敏捷开发方法(反复试错)开始开发自己的代码的很好的方式 |
接下来,我们会学习如何通过网络爬虫和 API 收集更多的数据。你在本章学到的知识会用在今后收集的数据上,所以请继续阅读,学习新的演示技巧。在后面的章节里,你会学得更高级的 Python 数据技术,让你更好地使用 Python 收集、评估、保存和分析数据。你在本章学到的讲故事工具会帮助你更好地进行数据处理,将研究所得分享给听众和全世界。