惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Project Zero
Project Zero
S
Security @ Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
A
Arctic Wolf
Webroot Blog
Webroot Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
Security Latest
Security Latest
H
Heimdal Security Blog
N
News and Events Feed by Topic
N
News | PayPal Newsroom
T
Tor Project blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
The Last Watchdog
The Last Watchdog
Y
Y Combinator Blog
宝玉的分享
宝玉的分享
Scott Helme
Scott Helme
A
About on SuperTechFans
M
MIT News - Artificial intelligence
V
V2EX
V
Visual Studio Blog
Recorded Future
Recorded Future
博客园 - 叶小钗
F
Fortinet All Blogs
L
Lohrmann on Cybersecurity
The GitHub Blog
The GitHub Blog
博客园 - Franky
P
Proofpoint News Feed
MyScale Blog
MyScale Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Secure Thoughts
D
DataBreaches.Net
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 三生石上(FineUI控件)
I
InfoQ
SecWiki News
SecWiki News
Blog — PlanetScale
Blog — PlanetScale
Engineering at Meta
Engineering at Meta
J
Java Code Geeks
B
Blog RSS Feed
AWS News Blog
AWS News Blog
Know Your Adversary
Know Your Adversary
V
Vulnerabilities – Threatpost
H
Help Net Security

少数派

派早报:Google 发布 Fitbit Air 等 - 少数派 「新人报到」確認需求,再開始 - 少数派 从 SOLO 独立开发者社区,我看到了越来越多开发者开始做自己的产品 - 少数派 我怎么管理那些"不常做,但总会忘"的生活事项 - 少数派 人形机器人量产元年,数据才是具身智能的“生死线” - 少数派 BuhoLaunchpad 高度还原 Mac 启动台:开发历程与思考 - 少数派 五年陪伴依然不舍,DIY 换壳后让罗技 MX Master 3 继续服役 - 少数派 新玩意 240|少数派的编辑们最近买了啥? - 少数派 一日一技|为什么你应该关闭 iOS 的键盘声音 - 少数派 我做了个插件和 Skills,一键提取任何网站的设计规范 Design.md - 少数派 住在三四线城市的你,该开始录播客了 - 少数派 甘南秘境,大白高国 - 少数派 AI的审美:谁让把我变成川内倫子 - 少数派 返工怎能不烦恼,打工人片单总有一部是你的「嘴替」 - 少数派 为了让「上厕所」更健康,我做了一个小工具 - 少数派 AI + Skill,能够让生成的文章去除 AI 味吗? - 少数派 新玩意|韶音OpenDots ONE 耳夹式耳机 - 少数派 《美满》| 在每一个春天的晚上相爱(362) - 少数派 新玩意|优篮子 PS01 MagSnap 磁吸支架 - 少数派 自我整合手记 | 我开始早睡了:用稳定规则,为自由托底 - 少数派 用龙虾(OpenClaw)两个多月,我最深的12个体会 - 少数派 听歌时间到,12 张你可能错过的 2025 华语乐坛好专辑 - 少数派 承诺能追吗 - 少数派 macOS 26启动台没了? 我做了个不一样的App启动器 - Keboard - 少数派 《四海为家的人》| INTJ对话INTJ(361) - 少数派 你发过的那些黑历史,是时候一次清干净了 - 少数派 新玩意:安安静静玩,越玩越专注:计客密码机 - 少数派 iPad 用户首次体验 Android 平板:vivo Pad6 Pro - 少数派 数据逻辑强 - 少数派 极北行+ | 一路向北,探访日本至北之地 | 001 - 少数派 万字剖析:千问App深度体验报告(2026) - 少数派 在2026年,如何真正防止别人抄袭你的作品 - 少数派 怎么用 50 块搭个 AI 语音助手?我踩了 3 天坑 - 少数派 YeeroAI:让 AI 对话真正成为知识管理的一部分 - 少数派 爬泰山 - 少数派 「旅图显影」 App 更新:这次,我们补上了一点「手感」 - 少数派 假期出门太折磨?我的 23 条经验帮你规划惬意旅行 - 少数派 工作流会变吗 - 少数派 Claude Opus 4.6 怎么用最省钱?我测了 5 种方案 - 少数派 GPT Image 2 让图文并茂不再稀罕 - 少数派 用户侧出发——什么是AI,我要不要学习? - 少数派 找片、转存、整理、播放一条龙!让你的付费网盘值回票价 - 少数派 欢迎试用!日课一问2.0插件 - 少数派 自己做的MDeditor,原本想购买 Typora 试了两次支付不成功,干脆自己做一个 - 少数派 vibe coding了一个 3MB 的小工具,让 ~/Downloads 彻底告别混乱 - 少数派 因为受不了 Mac 的风扇策略,我做了一个风扇控制工具 - 少数派 别只怪模型 - 少数派 Warp 终端的 AI 功能怎么用?我测了一周的体验 - 少数派 AI 写代码老是出 bug?这 5 个配置我后悔没早知道 - 少数派 「新玩意」苹果出相机可能就这样:Sigma BF + 45mm F2.8 DG Contemporary - 少数派 一个面向2030年的AI操作系统是什么样子的:浅谈cola这款有灵魂的Agent - 少数派 别只看写代码 - 少数派 每天解决10个问题,还是一口气攻坚解决400个? - 少数派 AI 交易机器人怎么搭?我用 Claude 跑了一周实盘 - 少数派 Maptoposter Online:把你爱的城市画成艺术海报 - 少数派 Function Calling 怎么用?我测了 3 个模型发现差距真大 - 少数派 Legend Talk:我做了个 AI 圆桌,让 160 位思想家围着你的问题转 - 少数派 如何找到自己的蓝方?在小县城寻找压力测试 - 少数派 语音输入与软件接口|2026年聊AI时,我们都聊些什么(上) - 少数派 混动已经卖爆,纯电又来补刀——钛7闪充版简直“不讲武德” - 少数派 本月玩什么|朋友收藏、识质存在、沙罗周期 - 少数派 为什么要每天坚持输出? - 少数派 Claude API 挂了好几个小时,你的项目有备用方案吗? - 少数派 Function Calling 没你想的复杂——我用它做了个有点用的工具 - 少数派 登录系统立即播放视频或者图片音乐的软件 - 少数派 我为什么创建 FlipHTML5 下载工具 - 少数派 残局没电?多品牌外设电量统一管理软件EasyBluetooth已支持RTSS游戏内显示以及AIDA64 - 少数派 前往通义路的路 - 少数派 太好看了,媲美Sun的个人导航页,NAS部署星云门户 - 少数派 乌黑嘴唇“一键检测”上线了 - 少数派 派早报:Claude AI 接入多个创意软件生态、FILCO 生产方接手品牌等 - 少数派 【更新】BearCLI、Claude 连接器与 MCP 服务器 - 少数派 记了上千条流水,还是看不懂财务?我做了一个让 AI 读懂账本的工作台 - 少数派 MINI R56 升级原厂 Sport 模式 - 少数派 新玩意 | 一棵柠檬树(仿真版) - 少数派 Momenta的“物理AI”野望,需迈过“含摩量”这道关 - 少数派 网页直接投屏控制手机!NAS一键部署PandaScrcpy,流畅丝滑可远程。 - 少数派 众测|邀你一同探索随身 AI 硬件入口 YoooClaw C·ONE - 少数派 2050大会:分享时间是真诚 参会记 - 少数派 iPad 赋能电影创作:国内首部宣纸手绘长片《燃比娃》的幕后故事 - 少数派 AI的审美:我用 8 个大模型给 100 张旅行照片打分 - 少数派 普通人如何破圈?去参加一个本地协会 - 少数派 把极空间的图标全换了,主题DIY全攻略打造你的专属NAS桌面 - 少数派 电子便签墙,帮你实现便签自由 - 少数派 我如何用三个 CLI 工具取代文档创建需求 - 少数派 原来真的有人可以玩一辈子 - 少数派 社区速递 139 | 派友热议三月买了啥、复古单反尼康 Df 体验 - 少数派 06 作品的赏析与评价 - 少数派 TDS REVIEW|索尼 WF-1000XM6 降噪真无线耳机体验 - 少数派 35.98万起售的第二代腾势D9,我看重的不是堆料,而是不凑合 - 少数派 鼠须管 Squirrel 皮肤配置指北 - 少数派 从watch ultra2换到redmi watch6 - 少数派 派早报:阿里巴巴发布视频生成模型 HappyHorse 1.0 等 - 少数派 别迷信1M - 少数派 家人们天塌了!网盘“大封杀”,多个渠道多条路,NAS部署PanHub - 少数派 AI与人勾心斗角!NAS一键部署AI狼人杀,假日休闲必备。 - 少数派 电商必备!Comfyui工作流批量生图插件,一次生成12张!支持Nano banana pro模型 - 少数派 Comfyui工作流配置Gpt-image-2模型教程,0.03/张 - 少数派 OpenClaw第三方APi怎么配置?可使用Gpt-image-2模型 - 少数派 会员社区话题精选 Ep. 103 - 少数派
预览
2018-07-31 · via 少数派

如果一幅图胜过千言万语,那么一幅会动的图呢?

我们绘制统计图形,显然不是给电脑看的。因为它看不懂,也没必要看。给它数据就好了。

绘制统计图形,是给人看的。可以给别人看,例如合作者、读者、审稿人,或者演讲时的观众。但更多的情况,图也是给自己看的。密密麻麻的数字或符号,远不如一幅图像,看得清楚和舒服。人类中的大多数,目前还没有进化出对海量原始数据,条件反射一般的理解能力。

因此人们常说「一图胜千言」。

而当图片变成动态的,它将能够进一步地,给我们提供更多一个维度的信息,比如这样:

GDP 和预期寿命之间的关联
GDP 和预期寿命之间的关联

这幅动态统计图,描绘了世界不同区域,人均 GDP 和预期寿命之间的关联。随着左上角年份的不断变化,你会看到几十年来,这个世界的发展变化。Hans Rosling 曾经用类似的数据和动画效果,做了非常精彩的 TED 演讲。我上课的时候,不止一次拿来作为演示样例,让学生揣摩学习。

null

你知道吗?只需要短短 10 行语句,你也能自己绘制出这个图形。

不过我们学东西,不宜贪多求快。要绘制上图,你需要了解相关的基础知识。一下子摄入很多新知,可能造成认知负荷,对学习兴趣没有益处。本文中,我用一个更简单的例子,给你展现如何用 R 绘制动态统计图。有了它作为基础,结合我给你推荐的相关学习资源,你也能很快做出更为实用,甚至是令人惊艳的动图。

预览

实际动手之前,我想让你看一下,用 R 可以绘制出什么样的动态统计图效果。你不需要安装任何软件。只需要点击这个链接,就可以使用 R 编程环境了。

Binder 的界面
Binder 的界面

等准备工作完毕,你会看到,浏览器里面开启了一个 RStudio 界面。

RStudio
RStudio

点击左上角的 File -> Open File,并且从出现的文件列表中,选择 code.Rmd

选择 code.rmd
选择 code.rmd

你就能看见下图这样打开该文件后的结果。

打开文件后的结果
打开文件后的结果

Rmd 文件后缀,代表 R Markdown,是 RStudio 这个 IDE 上可以使用的一种特殊的 Markdown 文件。说它特殊,是因为其中的代码段落,可以直接运行出结果。

null

界面左上方这里,有一个毛线球形状的按钮,名称叫做 Knit ,点击一下,它会把这个 code.Rmd 文件,转换成 HTML ,并且其中全部的代码,都显示出运行结果来。这是第一页:

null

翻到最后一页,效果是这样的:

null

怎么样,挺有趣吧?想不想自己试试看?

上手

点击左上角的 File -> New File ,选择菜单里面的第一项 R Script

null

此时,你会看到左侧分栏一个空白编辑区域开启,可以输入语句了。

null

输入之前,我们先给文件起个名字。点击 File -> Save 按钮。

null

在新出现的对话框里面,输入 demo ,回车。

null

下面就可以输入并运行代码了。

其实我们下面输入的代码,就是刚才你在 code.Rmd 文件里见到的寥寥几个代码段落。而且,就连这几个代码段,也不是你将来绘制动态统计图都需要用到的。它们中的大多数内容,只是为了给你逐步展示方法,加入的中间过程而已。

如果你比较心急,只想获得最终的结果,那么你只需要录入以下这9行语句:

library("tidyverse")
library("lubridate")
library("gganimate")
load('carriers_jan.RData')
carriers_jan %>% 
  ggplot(aes(x=carrier, y=n, fill=carrier)) +
  geom_bar(stat='identity', position='identity') +
  transition_time(mydate) +
  labs(title='{frame_time}')

点击运行,就能做出你想要的结果了。

然而,这样一来,你还是不清楚具体语句的含义。将来没办法很好地应用到自己的实际工作和科研中去。所以如果你能抽出10分钟的时间,还是请按照我下面的说明来操作。根据教程,一步步手动输入语句。这样更有助于你的理解,收获会更大。

代码

首先,我们需要读入几个必要的软件包:

library("tidyverse")
library("lubridate")
library("gganimate")

如果你看过我的《如何用R和API免费获取Web数据?》一文,对于 tidyverse 应该并不陌生。它是大神 Hadley 等人共同开发的一系列 R 工具包合集。对我来说,它改变了之前 R 语言"难以学习"、"语法古怪"、"不好使用"等刻板印象。

lubridate 是用来处理时间数据的 R 软件包。如果没有这东西,你每次操作时间数据,都会麻烦许多。

gganimate 顾名思义,后面我们绘制动态图形,需要用到。

下面看看我们这次使用的数据。数据保存的格式是 .RData ,需要使用 load() 函数读入。

load('carriers_jan.RData')

读入以后,保存在其中的一个数据框变量 carriers_jan 就复活了。下面我们看看其内容:

carriers_jan
## # A tibble: 93 x 3

##    mydate     carrier     n

##    <date>     <chr>   <int>

##  1 2013-01-01 AA         94

##  2 2013-01-01 DL        112

##  3 2013-01-01 UA        165

##  4 2013-01-02 AA         94

##  5 2013-01-02 DL        152

##  6 2013-01-02 UA        170

##  7 2013-01-03 AA         95

##  8 2013-01-03 DL        128

##  9 2013-01-03 UA        159

## 10 2013-01-04 AA         95

## # ... with 83 more rows

这个数据实际上是从《如何用4行 R 语句,快速探索你的数据集?》一文中的 nycflights13 数据集,通过转换得来的。转换后的数据,统计了不同航空公司在2013年1月,每一天从纽约三大机场起飞航班次数。

为了简便,我们在这个数据集里,只保留了3家航空公司,即:

  • 美国航空(American Airlines,AA)
  • 达美航空(Delta Air Lines, DL)
  • 美联航(United Airlines, UA)

下面我们挑出1月1日的数据看看:

carriers_jan %>%
  filter(mydate == ymd('20130101'))
## # A tibble: 3 x 3

##   mydate     carrier     n

##   <date>     <chr>   <int>

## 1 2013-01-01 AA         94

## 2 2013-01-01 DL        112

## 3 2013-01-01 UA        165

可见,这一天里,美国航空起飞航班 94 架次,达美 112 ,美联航为 165 。

根据上表,我们绘制一张柱状图(bar chart)。横坐标是航空公司名称,是分类数据;纵坐标是航班次数,是量化数据。

carriers_jan %>%
  filter(mydate == ymd('20130101')) %>%
  ggplot(aes(x=carrier, y=n, fill=carrier)) +
  geom_bar(stat='identity', position='identity')
null

如上图所示,三家航空公司从纽约机场起飞次数,分别采用了不同颜色柱状图进行了可视化。红色是美国航空,绿色是达美航空,蓝色是美联航。

简单解释一下其中的 ggplot 语句。ggplot2 也是 Hadley Wickham 的作品,属于 tidyverse 软件包的一部分。它将 Leland Wilkinson 提出的"绘图语法"(Grammar of Graphics)在 R 语言上实现。

在《如何用 Python 和 API 收集与分析网络数据?》一文中,我们已经介绍过 ggplot2 的 Python 克隆(plotnine),所以这里就不赘述背景了。你只要记住,它绘制图形的时候,采用的是"分层"机制就好。

null

ggplot(aes(x=carrier, y=n, fill=carrier)) 这一句讲述映射(mapping)关系,指定了把 carrier 信息投射到 x 轴, n(航班次数)投射到 y 轴,用不同 carrier 类别填充不同的色彩。

但是单单这一句,实际上是绘制不出东西来的,不信你可以尝试执行一下:

carriers_jan %>%
  filter(mydate == ymd('20130101')) %>%
  ggplot(aes(x=carrier, y=n, fill=carrier))
null

请注意这个图里, x 轴和 y 轴的设置,都与我们的预期一致。但是任何实质性内容,都没有绘制出来。因为咱们还没有告诉 ggplot ,打算画一个什么类别的统计图形。这就是下一句 geom_bar(stat='identity', position='identity') 的用处。

这句话告诉 ggplot ,请绘制柱状图,柱的高度按照 y 值设置,对应 x 上每一个取值(航空公司名称),分别绘制一根柱。

这张静态图,只能告诉我们2013年1月1日这一天,纽约机场这3个航空公司起飞航班数量信息。假如我们想多了解一个维度,也就是把时间加进去,怎么办?

这里办法并不唯一。

最简单的常规方法,是把三维信息压缩到二维平面里面去。因为我们看二维图像,除了能观察到位置区别之外,还可以辨识色彩。利用下列语句,你可以把这张图轻松做出来。

carriers_jan %>%
  ggplot(aes(x=mydate, y=n, color=carrier)) +
  geom_point() + geom_line()
null

注意,这里因为我们不再把时间限定在1月1日了,因此你得把 filter(mydate == ymd('20130101')) 这一句去掉,使用全部1个月的时间。否则使用时间轴就没有意义了。

这里的 ggplot(aes(x=mydate, y=n, color=carrier)) ,你应该能观察到跟之前的图形间,映射关系的差别。不同于上一幅图,我们把 mydate ,而不是 carrier 映射到了 x 轴。 y 轴的映射关系没有变化。

我们此次不打算绘制柱状图了,而是描绘随时间变化趋势,所以选用的是散点图(geom_point())+折线图(geom_line())。这就意味着,再考虑柱状图里面的填充,就不恰当了,所以我们把 carrier 的信息,映射到颜色上去(color=carrier)。

从这张图里,你可以发现非常显著的规律性。假如你不想这样压缩信息,而希望用图形随时间的动态变化,来体现附加的时间维度,该怎么办?

这时,你就需要使用 gganimate 这个动画包的功能了。gganimate 目前的开发维护者,是 Thomas Lin Pedersen 。这是他的 github 页面地址。

null

他把原先的 gganimate 包接管了过来,仿照 ggplot 的风格,对语法进行了修改和补充,使其能够无缝融入到 ggplot 语句里,很方便地调用。因为可以用动态体现时间维度,所以我们这次依然绘制柱状图。语句如下:

carriers_jan %>%
  ggplot(aes(x=carrier, y=n, fill=carrier)) +
  geom_bar(stat='identity', position='identity') +
  transition_time(mydate)
null

图动起来了,是吧?

解释一下语句。

与之前静态柱状图的区别,也是去掉了时间的限定那一句 filter(mydate == ymd('20130101')) ,以便描绘整个儿一月份的情况。另一个显著差别,是加入了最后一行语句, transition_time(mydate) ,这也是图像能够动起来的关键。

根据 gganimate 官方的说明,图形转换可以有多个不同类型语句来控制。因为我们恰好有 mydate 这个时间数据列,所以可以使用最自然而简单的 transition_time() 方法。transition_time(mydate) 根据时间信息对数据框进行切片,然后分别加以展示。图像因而动了起来。

不过,这里有个很严重的问题------你根本就看不清,当前的动态结果对应哪个时间。对不对?

咱们需要改进一下。改进的方法很简单:加入图片标题,显示时间,并且让标题对应着一起变化。修改后的代码如下:

carriers_jan %>%
  ggplot(aes(x=carrier, y=n, fill=carrier)) +
  geom_bar(stat='identity', position='identity') +
  transition_time(mydate) +
  labs(title='{frame_time}')
null

这下,你一眼就可以从标题中,看到当前动图对应的时间了。这里我们用到了 ggplotlabs() 函数,这个函数负责图片的标记设定,除了标题以外,你还可以设置横纵轴说明等内容。

我们用 title 参数设置标题内容。标题需要变化,所以我们得传入一个可以变化的量title 参数。我们传入的是 {frame_time} ,这就是我们刚才提到的, gganimate 自动切片所用的时间数据。 传入参数时,不要忘了需要将其包裹在双引号里,作为字符串类型传入。

小结

本文给你展示了 R 环境绘制动态统计图的方法,具体包含以下知识点:

  • 如何读入 .RData 格式的数据文件;
  • 如何利用 ggplot 命令映射变量,选择统计图类型(包括柱状图、散点图和折线图等);
  • 如何使用 gganimatetransition_time() 方法绘制基于时间数据的动态图;
  • 如何通过 labs 设置,动态显示时间,以便于和图像的变化对应。

为了展示样例的最小化,本文的动态统计图非常简单,技术含量并不高。抛砖引玉。希望你举一反三,绘制出更有价值、内容也更加丰富的动态统计图来。

如果你对 ggplot2 绘图包感兴趣,想详细了解其语法,可以读作者 Hadley Wickham 自己写的书《ggplot2:数据分析与图形艺术》。

null

如果你想了解 gganimate 包的更多用法,可以阅读官方文档,或者看这段作者的演讲视频

null

希望这些资源,能对你今后可视化沟通、展示自己的数据分析结果,有所帮助。

给你留个思考题:

本文中的数据,是从《如何用4行 R 语句,快速探索你的数据集?》一文中的 nycflights13 数据集,通过转换(data manipulation)得来的。你能不能自己利用 R 或者 Python 语句,完成这一转化过程呢?

欢迎留言,把你的思考和解决过程分享给大家。

小提示:

喜欢请点赞。还可以微信关注和置顶我的公众号“玉树芝兰”(nkwangshuyi)

如果你对数据科学感兴趣,不妨阅读我的系列教程索引贴《如何高效入门数据科学?》,里面还有更多的有趣问题及解法。