场景2:你如何制作一个散点图?
MPL(看起来有点震惊):我的意思是,你可以再次用for循环。当然,这样就可以了。当然可以。看到了吗?(降低声音说)只要记得明确地设置颜色参数否则这些点都是蓝色的......
# MATPLOTLIB
fig, ax = plt.subplots(1, 1, figsize=(7.5, 7.5))
for i, s in enumerate(df.species.unique()):
tmp = df[df.species == s]
ax.scatter(tmp.petalLength, tmp.petalWidth,
label=s, color=cp[i])
ax.set(xlabel='Petal Length',
ylabel='Petal Width',
title='Petal Width v. Length -- by Species')
ax.legend(loc=2)
MPL:但是,额,(假装很自信)我有个更好的方法!看!
# MATPLOTLIB
fig, ax = plt.subplots(1, 1, figsize=(7.5, 7.5))
def scatter(group):
plt.plot(group['petalLength'],
group['petalWidth'],
'o', label=group.name)
df.groupby('species').apply(scatter)
ax.set(xlabel='Petal Length',
ylabel='Petal Width',
title='Petal Width v. Length -- by Species')
ax.legend(loc=2)
MPL:这里,我定义了一个名为scatter的函数。它用 pandas 的 groupby 对象得到分组,然后在 x 轴上画出花瓣长度,y 轴则是花瓣宽度。每组都如此处理一次!厉害吧!
P:太好了,Mat!太棒了!本质上,我要做的事情是一样的,所以我就不展示了。
SB(咧嘴笑):这次没有轴向旋转?
P:嗯,在这种情况下,轴向旋转是复杂的。我们不像处理时间序列数据集的时候一样有一个通用的索引,所以——
MPL:嘘!我们不需要向她解释。
SB:不管。不管怎样,在我看来,这个问题和上一个问题是一样的。构建另一个FacetGrid,但借用plt.scatter而非plt.plot。
# SEABORN(代码部分)
g = sns.FacetGrid(df, hue='species', size=7.5)
g.map(plt.scatter, 'petalLength', 'petalWidth').add_legend()
g.ax.set_title('Petal Width v. Length -- by Species')
GG:是的!是的!同样!你只要把geom_line换成geom_point就行了!
# GGPY
g = ggplot(df, aes(x='petalLength',
y='petalWidth',
color='species')) + \
geom_point(size=40.0) + \
ggtitle('Petal Width v. Length -- by Species')
g
ALT(看起来很困惑):是的,把我们的mark_line换成mark_point。
# ALTAIR
c = Chart(df).mark_point(filled=True).encode(
x='petalLength',
y='petalWidth',
color='species')
c
分析场景2
在这里,从你的数据之中构建API的潜在的复杂性变得清晰了。虽然Pandas的旋转技巧对于时间序列来说是非常方便的,但它在这个例子中并不能很好地应用。
公平地说,group by方法是具有普适性的,而for循环方法的普适性很强;然而,它们需要更多的自定义逻辑,而自定义逻辑需要自定义工作: Seaborn已经做好了,要不然你还得自己造个轮子。
相反,Seaborn、ggpy和Altair都意识到,散点图很大程度上就是没有假设的折线图(不管这些假设是多么的无害)。因此,我们在场景1中的代码可以很大程度上被重用,但是使用新的geom(ggpy/altair的geom_point/mark_point)
或新方法(Seaborn的plt.scatter)。在这个组合中,似乎哪一个工具表现得比其他的便利更多,尽管我喜欢Altair的优雅简洁。
场景三:你将如何画分面的散点图?
MPL:嗯,一旦你掌握了for循环——显然我已经掌握了——那么这就只需要对我之前的代码做出一个的简单调整。我不是用我的subplot方法构建一个坐标轴,而是构建三个。
接下来,我像以前一样遍历一遍,采用取数据子集的办法来取Axes对象的子集
(信心恢复)我敢打赌你们之中没人能提出一个更简单的方法——举起手臂,在这一过程中几乎击中了Pandas。
# MATPLOTLIB
fig, ax = plt.subplots(1, 3, figsize=(15, 5))
for i, s in enumerate(df.species.unique()):
tmp = df[df.species == s]
ax[i].scatter(tmp.petalLength, tmp.petalWidth, c=cp[i])
ax[i].set(xlabel='Petal Length',
ylabel='Petal Width',
title=s)
fig.tight_layout()
SB和ALT对视了一眼,ALT开始笑了;GG仿佛像听见什么笑话一样大笑起来。
MPL:怎么了?
Altair:检查你的x轴和y轴,老兄。你所有的图表的坐标轴取值范围都不一样。
MPL(脸变红了):啊,当然,这只是确认你们是否集中注意力的一个测试。你可以,呃,通过在subplot函数中指定坐标轴范围,来确保所有的子图中坐标轴都有相同的范围。
# MATPLOTLIB
fig, ax = plt.subplots(1, 3, figsize=(15, 5),
sharex=True, sharey=True)
for i, s in enumerate(df.species.unique()):
tmp = df[df.species == s]
ax[i].scatter(tmp.petalLength,
tmp.petalWidth,
c=cp[i])
ax[i].set(xlabel='Petal Length',
ylabel='Petal Width',
title=s)
fig.tight_layout()
P(叹息):我也会这么做。跳过我吧。
SB:改写FacetGrid以应用于这种情况很简单。就像hue参数一样,我们可以简单地添加一个“col”参数。这会让FacetGrid不仅为每个种类分配了一种唯一的颜色,而且还将每个种类画在唯一的子图上,按列排列。(我们可以通过将col参数换成row参数,就可以按行来排列它们)
# SEABORN
g = sns.FacetGrid(df, col='species', hue='species', size=5)
g.map(plt.scatter, 'petalLength', 'petalWidth')
GG:哦,这和我做的不一样。(再次拿起ggplot2开始读)看,分面和图形映射本质上是两个不同的步骤,我们不应该一时疏忽把它们混为一谈。因此,我们接着用之前的代码,这次加上 facet_grid 层,也就是显式地用类别进行分面。(开心地合上书)至少我大哥是这么说的!你们听说过他吗?他真酷啊。
# GGPY
g = ggplot(df, aes(x='petalLength',
y='petalWidth',
color='species')) + \
facet_grid(y='species') + \
geom_point(size=40.0)
g
ALT:我在这里采用了一种类似于Seaborn的方式。具体来说,我只是在encode函数中加入一个column参数。也就是说,我在这里也做了一些新东西:
# ALTAIR
c = Chart(df).mark_point().encode(
x='petalLength',
y='petalWidth',
color='species',
column=Column('species',
title='Petal Width v. Length by Species'))
c.configure_cell(height=300, width=300)
分析场景3
Matplotlib说得很好:在这种情况下,他的代码根据分类对数据进行分面的思路和上面的其他方案是一样的;假如你的脑袋可以搞清楚那些 for 循环的话,你可以再试试下面这段代码。但是我可没有让他再搞出更复杂的东西出来,比如 2 x 3 的网格。不然他就得像下面这样干:
# MATPLOTLIB
fig, ax = plt.subplots(2, 3, figsize=(15, 10), sharex=True, sharey=True)
# this is preposterous -- don't do thisfor i, s in enumerate(df.species.unique()):
for j, r in enumerate(df.random_factor.sort_values().unique()):
tmp = df[(df.species == s) & (df.random_factor == r)]
ax[j][i].scatter(tmp.petalLength,
tmp.petalWidth,
c=cp[i+j])
ax[j][i].set(xlabel='Petal Length',
ylabel='Petal Width',
title=s + '--' + r)
fig.tight_layout()
使用正规的可视化表达:使用Altair的话,这一切都会非常简单:
# ALTAIR
c = Chart(df).mark_point().encode(
x='petalLength',
y='petalWidth',
color='species',
column=Column('species',
title='Petal Width v. Length by Species'),
row='random_factor')
c.configure_cell(height=200, width=200)
只比上面的encode函数多一个参数!
希望在你的可视化库框架中植入分面功能的好处已经明晰了。
第二幕:分布和条形图
场景四:你会如何可视化数据分布?
MPL(信心明显动摇):嗯,如果我们想要一个箱形图——我们想要一个箱形图吗?
——我有一个方法。它很愚蠢,你会讨厌它:我将数组组成的数组传递到boxplot方法中,这就会为每个子数组生成一个箱线图。你得自己手动给X轴标刻度。
# MATPLOTLIB
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
ax.boxplot([df[df.species == s]['petalWidth'].values
for s in df.species.unique()])
ax.set(xticklabels=df.species.unique(),
xlabel='Species',
ylabel='Petal Width',
title='Distribution of Petal Width by Species')
MPL:如果我们想要一个直方图——我们想要一个直方图吗?我也有一个方法,你可以用之前提到的for循环或group by来生成。
# MATPLOTLIB
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
for i, s in enumerate(df.species.unique()):
tmp = df[df.species == s]
ax.hist(tmp.petalWidth, label=s, alpha=.8)
ax.set(xlabel='Petal Width',
ylabel='Frequency',
title='Distribution of Petal Width by Species')
ax.legend(loc=1)
P(看起来异常自豪):哈!哈哈哈!这是我的时刻!你们都以为我不过是matplotlib库的附庸,虽然我到目前为止还只是套用他的plot方法,但我也有一些能够处理箱型图和直方图的函数——这些都使得可视化数据分布变得非常简单。
你只需要两个东西:
# PANDAS
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
df.boxplot(column='petalWidth', by='species', ax=ax)
# PANDAS
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
df.hist(column='petalWidth', by='species', grid=None, ax=ax)
GG和ALT击掌祝贺P:“太棒了!”,“就该这样!”,“就这么干!”。
SB(假装热情):哇。很棒。在我的世界里,分布非常重要,所以我为它们准备了一些特殊的函数。例如,我的boxplot方法需要一个x参数、一个y参数和数据,结果如下:
# SEABORN
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
g = sns.boxplot('species', 'petalWidth', data=df, ax=ax)
g.set(title='Distribution of Petal Width by Species')
SB:这个,我是说,有些人告诉我这个很漂亮……不管了。我还有一种特殊的分布方法,叫做“distplot”,它超越了直方图(傲慢地看着Pandas)。你可以用它来画直方图,KDEs,和轴须图(rugplots),甚至是画到一块。比如说,通过与FacetGrid结合,我可以为每一种鸢尾花都画出直方轴须图:
# SEABORN
g = sns.FacetGrid(df, hue='species', size=7.5)
g.map(sns.distplot, 'petalWidth', bins=10,
kde=False, rug=True).add_legend()
g.set(xlabel='Petal Width',
ylabel='Frequency',
title='Distribution of Petal Width by Species')
SB:但是......不管了。
GG:这个只不过是新的geom而已!用GEOM_BOXPLOT来画箱型图,用GEOM_HISTOGRAM来画直方图!换用它们就可以了!(开始绕着餐桌跑来跑去)
# GGPY
g = ggplot(df, aes(x='species',
y='petalWidth',
fill='species')) + \
geom_boxplot() + \
ggtitle('Distribution of Petal Width by Species')
g
# GGPY
g = ggplot(df, aes(x='petalWidth',
fill='species')) + \
geom_histogram() + \
ylab('Frequency') + \
ggtitle('Distribution of Petal Width by Species')
g
ALT(看起来很有信心和自信):我……我要忏悔……
沉默降临——GG停止奔跑,使得盘子掉到了地上。
ALT:(深呼吸)我……我……我不能做箱型图。我从来没有真正学会怎么画,但是我相信我的源语言JavaScript的语法不支持箱型图是有理由的。我倒是可以画一个均值直方图。
# ALTAIR
c = Chart(df).mark_bar(opacity=.75).encode(
x=X('petalWidth', bin=Bin(maxbins=30)),
y='count(*)',
color=Color('species', scale=Scale(range=cp.as_hex())))
c
ALT:乍一看,代码可能很奇怪,但不要惊慌。这实际上是说,直方图实际上就是条形图。X轴对应着间距,我们可以用Bin类来定义;与此同时Y轴对应落入相应间距的数据量,用SQL语言来说,就是y就是“count(*)”。
分析场景四
在工作中,我发现pandas库的功能非常便利方便;然而,我承认确实有一些记忆上的负担,那就是pandas库制作箱型图和直方图时包含参数“by”,但折线图却没有。
我将第1幕与第2幕分开的原因有几个,其中最重要的原因是:第2幕中使用matplotlib库的时候特别麻烦。例如,当你想要一个箱型图的时候,你还得记得用一个独立的界面,这完全不适合我。
说到第一幕和第二幕,有一个有趣的故事:我实际上是它丰富的“专利级”可视化函数(如,displot,小提琴图,回归图等)才从matplotlib/pandas库转向Seaborn的。虽然后来我喜欢上了FacetGrid,但我坚持认为第2幕所呈现的这些功能才是Seaborn的杀手锏。只要我还在绘图,我就还是Seaborn的粉丝。
(此外,我需要说明:Seaborn实现了很多被小型库忽略的极好的可视化功能;如果你正好需要其中一两种,那么Seaborn就是你唯一的选择。)
这些例子真的能够让你体会到ggpy的geom系统的力量。使用几乎相同的代码(更重要的是,连思维过程都基本相同),我们创建了一个完全不同的图。我们不是通过调用一个完全独立的函数,而仅仅是改变图形映射到视图的方式,比如说将geom换成另一个。
类似地,即使是在第二幕的世界里,Altair库的API接口也具有非同寻常的一致性。即使是对于不太寻常的操作操作,Altair库的API接口也很简单、优雅,令人印象深刻。
数据说明
| survived | pclass | sex | age | fare | class |
0 | 0 | 3 | male | 22.0 | 7.2500 | Third |
1 | 1 | 1 | female | 38.0 | 71.2833 | First |
2 | 1 | 3 | female | 26.0 | 7.9250 | Third |
3 |
1 | 1 | female | 35.0 | 53.1000 | First |
4 | 0 | 3 | male | 35.0 | 8.0500 | Third |
在最后一个场景中,我们将处理“泰坦尼克”,这是另一个著名的数据集,不过,在我们的代码中,我们称它为“df”。预览如下:
在这个例子中,我们将研究所付票费的均值,通过阶层和是否幸存进行分类。很明显,你可以使用pandas库来做这个。
dfg = df.groupby(['survived', 'pclass']).agg({'fare': 'mean'})
dfg
|
| fare |
survived | pclass |
|
0 | 1 | 64.684008 |
2 | 19.412328 |
3 | 13.669364 |
1 | 1 | 95.608029 |
2 | 22.055700 |
3 | 13.694887 |
这又有什么乐趣呢?这可是一个关于可视化的帖子,让我们以条形图的形式来呈现吧!
场景5:你如何画条形图?
MPL(表情冷酷):无可奉告。
# MATPLOTLIB
died = dfg.loc[0, :]
survived = dfg.loc[1, :]
# more or less copied from matplotlib's own# api example
fig, ax = plt.subplots(1, 1, figsize=(12.5, 7))
N = 3
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars
rects1 = ax.bar(ind, died.fare, width, color='r')
rects2 = ax.bar(ind + width, survived.fare, width, color='y')
# add some text for labels, title and axes ticks
ax.set_ylabel('Fare')
ax.set_title('Fare by survival and class')
ax.set_xticks(ind + width)
ax.set_xticklabels(('First', 'Second', 'Third'))
ax.legend((rects1[0], rects2[0]), ('Died', 'Survived'))
def autolabel(rects):
# attach some text labels
for rect in rects:
height = rect.get_height()
ax.text(rect.get_x() + rect.get_width()/2., 1.05*height,
'%d' % int(height),
ha='center', va='bottom')
ax.set_ylim(0, 110)
autolabel(rects1)
autolabel(rects2)
plt.show()
其他人都摇了摇头。
P:我需要先做一些数据处理——也就是group by和pivot——只要这样做了,我就有一种非常酷的方式来画条形图了——比上面的那个乱码要简单得多!哇,我感觉更有信心了,我把他们都比下去了!
# PANDAS
fig, ax = plt.subplots(1, 1, figsize=(12.5, 7))# note: dfg refers to grouped by# version of df, presented above
dfg.reset_index().\
pivot(index='pclass',
columns='survived',
values='fare').plot.bar(ax=ax)
ax.set(xlabel='Class',
ylabel='Fare',
title='Fare by survival and class')
SB:我恰好觉得这样的任务是极其重要的。因此,我实现了一个名为“factorplot”的特殊函数来解决这个问题:
# SEABORN
g = sns.factorplot(x='class', y='fare', hue='survived',
data=df, kind='bar',
order=['First', 'Second', 'Third'],
size=7.5, aspect=1.5)
g.ax.set_title('Fare by survival and class')
SB:跟之前一样,先将未处理的数据传给数据框。接下来,再说明你将用什么进行分组——在本例中,它是“Class”(阶级)和“Survived”(是否幸存),对应x和hue变量。接下来,说明你想要进行统计的数据列,这里是“Fare”(费用),所以这就变成了我们的y参数。默认的汇总统计是计算平均值,但是factorplot提供了一个名为estimator的参数,可以通过这个参数制定函数,例如,求和、标准差、中值等等,所选择的函数将决定每个条形的高度。
当然,有很多方法能够可视化这些信息,条形只是其中一种。因此,我还有一个参数“kind”用以制定不同的可视化方式。
最后,我们中有一些人比较关心统计上的准确性,所以在默认情况下,我会加上误差线,这样你就可以看到,不同的阶级和幸存率与所付票费是否有关系。(压低声音说)希望你们之中任何一个能做得比我还好。
ggplot2停下他的兰博基尼车,走了进来。
ggplot2:嘿,你们都看到了——
GG:嘿,兄弟。
GG2:嘿,小家伙。我们得走了。
GG:等等,我得快点儿把这个条形图画好,但是我被难住了。你会怎么做?
GG2(阅读说明):啊,像这样:
# GGPLOT2
# in R, I believe you'd do something like this:
ggplot(df, aes(x=factor(survived), y=fare)) +
stat_summary_bin(aes(fill=factor(survived)),
fun.y=mean) +
facet_wrap(~class)
# damn ggplot2 is awesome...
GG2:看到了吗?你要像我们一直所说的那样定义好图形映射,但是你得将y映射到平均费用上。为了做到这一点,就得叫我的朋友“stat_summary_bin”来做这件事,我只需要“mean”传递给“fun.y”参数就好了。
GG(眼睛瞪得大大的):哦,哇。我还没有“stat_summary_bin”呢。我猜——pandas,你能帮我一下吗?
P:哦,当然。
GG:耶!
# GGPY
g = ggplot(df.groupby(['class', 'survived']).\
agg({'fare': 'mean'}).\
reset_index(), aes(x='class',
fill='factor(survived)',
weight='fare',
y='fare')) + \
geom_bar() + \
ylab('Avg. Fare') + \
xlab('Class') + \
ggtitle('Fare by survival and class')
g
GG2:哈,不完全是图形式的语法,但是我想,只要Hadley(译者注:Hadley Wickham,ggplot2的开发者)没发现的话,(使用ggplot2)也能够良好运行。特别指出,你不应该在可视化之前就汇总数据。我也很困惑在这种语境下“weight”是什么意思。
GG:好吧,默认情况下,我的柱形对象使用简单计数,所以如果没有“weight”,所有的柱形高度都为1。
GG2:啊,我明白了。我们以后再谈吧。
GG和GG2说了再见,离开了宴会。
ALT:啊,这可是我的安身立命之道呢。很简单的。
# ALTAIR
c = Chart(df).mark_bar().encode(
x='survived:N',
y='mean(fare)',
color='survived:N',
column='class')
c.configure_facet_cell(strokeWidth=0, height=250)
ALT:我希望所有的参数都是直观的:我想以阶级进行分面,然后按照幸存与否进行分类,画出平均的价格。于是“幸存与否”就是x的参数,“平均数(费用)”作为y的参数,和“阶级”作为column的参数(我还指定了color参数,这样画面更飘逸一点)。注意,我在x和color参数的“survived”字符串后面加了“:N”,这是给我自己加的注释,代表“这是一个名义上的变量”。我把它放在这儿,是因为“survived”看上去像是定量变量,而定量变量会让这个图变得有点丑。不要被吓到,一直以来都是这样——只是没有明说而已。比如说,在上面的时间序列图中,如果我不知道“dt”是一个临时变量,我就会假设它们是名义变量,就会变得很尴尬(至少在我添加“:T”来清除这些变量之前)。另外,我调用我的configure_facet_celll协议,使我的三个子图看起来更加统一。
分析场景5
不要想太多:我再也不用matplotlib库画柱形图,而且要弄清楚,这并不是我一个人的观点!事实是:与其他库不同,matplotlib库不具备对所接收数据做出推测的功能。有时,这意味着你得写严格的命令式代码。
(当然,正是这种数据不可知论使得matplotlib库成为了构建其他Python可视化库的基础。)
相对而言,每当我需要汇总统计和误差线时,我总是会使用Seaborn库。
(这可能是不公平的,我选择了一个仿佛为Seaborn库的一个函数量身定做的例子,不过它在我的工作中经常出现,嘿,这篇博客可是我写的。)
我既没有发现pandas库的特别优势,也没有发现ggpy库具有特别的优势。
然而,就pandas库而言,即使是画简单的条形图,也得用group by和pivot,看上去有点儿傻。
同样,我觉得这是yhat开发的ggpy库的主要漏洞——要找到一个“stat_summary”的替代函数来全面完善功能还有很长一段路要走。
与此同时,Altair库依然让人印象深刻!这个例子中代码的直观程度让我印象深刻。即使你以前从未见过Altair库,我也能想象你能凭直觉知道发生了什么。这种思考、代码和可视化的一一对应正是我最喜爱之处。
总结
你知道,有时候我觉得心怀感激是很重要的:我们有很多很棒的可视化库可以选择,我喜欢深入挖掘它们!
(是的,这只是一种逃避。)
虽然我在matplotlib库上碰到了一点困难,它还是很有趣的(每一部戏都需要喜剧效果)。
这不仅是因为matplotlib库是pandas、Seaborn和ggpy库的基础,还因为它能够给予你必不可少的细粒度的控制权。这篇文章不涉及这个内容,但是在不使用altair的情况下,我都使用matplotlib来调整图形。但是——这是一个很重要的“但是”——matplotlib库是纯声明式的,非常仔细地指定可视化的方方面面会变得很乏味(参见:柱形图)。
确实,可能还有这种结果:“用统计可视化能力来评价matplotlib是不公平的,你这个刻薄的家伙。你在使用它的某一个用例和其他库的主要用例进行比较。这些方法显然需要协同工作。你可以使用自己喜欢的方便的/陈述性表示层——pandas库、Seaborn库、ggpy或者Altair(见下文)——来完成那些基础性的工作。然后你可以使用matplotlib库来完成非基础的部分。如果你穷尽了其他的库的力量,你会很高兴地发现拥有无限的力量的matplotlib就在你的身边,你这个不知感恩的业余绘图的。”
我想说的是:是的!这很有道理,但却是脱离现实的。尽管只是说这并不足以构成一篇博客文章的主要内容。
另外,要是我就不会骂人。
与此同时,在时间序列图中,轴向旋转加上pandas就非常好用。考虑到pandas对时间序列的支持范围更广,我还会接着用。此外,下一次如果要画RadViz
https://link.juejin.im/?target=http%3A%2F%2Fpandas.pydata.org%2Fpandas-docs%2Fstable%2Fvisualization.html%23radviz
图时,我就知道怎么做了。也就是说,虽然pandas确实通过提供基本的声明式语法(见柱形图)来改进matplotlib的命令式范式,但它仍然是matplotlib式的。
继续:如果你想要做更多偏向统计的事情,就用Seaborn吧(她在国外确实学到了很多很酷的东西)。学习她的API接口——factorplot、regplot、displot等等——然后爱上她。这将是值得的。至于分面,我认为FacetGrid是很有用的。要不是我使用Seaborn库那么长时间了,我可能会更喜欢ggpy或Altair库的。
说到声明式的优雅,我一直很喜欢ggplot2,而且对Python的ggpy印象深刻。我肯定会继续关注这个项目。(更自私的说,我希望它能阻止那些使用R语言的同事取笑我。)
最后,如果你想要做的事情是Altair可以完成的(对不起了,箱型图的使用者),用它吧!它拥有一个异常简单和好用的API接口。如果你需要额外的动力,想想以下几点:Altair库一个令人兴奋的特性是——除了即将对其基层的Vega-Lite语法进行改进之外——从技术上讲,它并不是一个可视化库。它会输出符合Vega-Lite标准的JSON对象,在笔记本(IPython Notebook)中可以用IPython Vega渲染得很好。
为什么这令人兴奋?在底层,Altair的可视化看上去都是这样的:
当然,看起来并不令人兴奋,但请考虑它的含义:如果其他库对此感兴趣,他们也可以直接开发将这些JSON对象转化为可视化结果的新方法。这意味着你可以在Altair上搞定基本的工作,然后再深入底层,用matplotlib以获得更多的控制权。
我已经对此期待万分了。
说完这一切,再说一些临别的话:在Python里的可视化比任何一个男人、女人或者尼斯湖水怪都要宏伟,你得有选择地接受我上面所说的一切——包括代码和观点。记住:互联网上的一切都是谎言、谎言和统计数据。
我希望你喜欢这个书呆子气十足的“疯帽子”(注:爱丽丝中的疯帽子)的茶话会,也希望你学到了一些事情可以用到自己的工作中。
和往常一样,代码在GitHub
https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fdsaber%2Fpy-viz-blog
注释
感谢Thomas Caswell
https://link.juejin.im/?target=https%3A%2F%2Fplus.google.com%2F%2BThomasCaswell
他写的关于matplotlib库的特性上地评论,你绝对要读一读。这样你就能一睹远比我上面所提供的优雅得多的matplotlib代码了。
我写这篇文章的时候,yhat将库的的名字从“ggplot”改为“ggpy”。我(认为我)相应地改变了所有的参考资料。
严格地说,这个故事不是真的。我几乎总是用Seaborn库,但是在需要定制的时候,我就会深入到matplotlib库。也就是说,我只是觉得这个matplotlib设定更为引人入胜,毕竟我们生活在一个后真相社会。
马上解释一下,你都生我的气了,所以请允许我解释一下:我爱bokeh和plotly。实际上,我在提交分析之前最爱做的一件事就是把图像传给相关的 bokeh/plotly 函数,获得自由的交互性;但是我对它俩都不是特别熟,没法做更高级的操作。(老实说,这篇文章已经够长了。)显然,如果你需要的是交互式可视化(而非统计可视化),那么你就得找它们。
请注意:这只是为了好玩。我没有用业余的拟人化手法评价任何库。我相信显示生活中的 matplotlib 是非常可爱的。
坦率地说,我不是完全确定单独进行分面操作是为了意识形态上的纯洁,或者只是单纯出于实用的考虑。虽然我的 ggplot 角色声称他是前者(他的理解来自匆匆读完的这篇论文(https://link.juejin.im/?target=http%3A%2F%2Fvita.had.co.nz%2Fpapers%2Flayered-grammar.pdf),也有可能是因为(实际上) ggplot2 对分面的支持太丰富了,所以需要当作是独立的步骤。如果我描述的角色违反了任何图形语法规则,请务必告诉我,我会去找个新的。
这个故事绝对没有任何道德含义。
笪洁琼,中南财大MBA在读,目前研究方向:金融大数据。目前正在学习如何将py等其他软件广泛应用于金融实际操作中,例如抓包预测走势(不会预测股票/虚拟币价格)。可能是金融财务中最懂建筑设计(风水方向)的长腿女生。花式调酒机车冲沙。上赛场里跑过步开过车,商院张掖丝路挑战赛3天徒步78公里。大美山水心欲往,凛冽风雨信步行。