今天介绍一本非常不错的书,英文名是Data Science at the Command Line。书中介绍了如何用命令行来获取、处理和展示数据,效率远远高于自己开发Python脚本来实现这些功能,所以非常值得学习。
1. Linux命令行介绍
Linux系统有许多命令行工具,这些工具可以完成非常强大的功能。如grep命令可以用来在文本中搜索特定的字符串,wc可以计算文本的行数,sort可以对文本进行排序等。命令行工具运行的时候是一个进程,每个进程都有三个标准的输入输出流,分别是标准输入*stdin, 标准输出stdout, 以及错误输出stderr*。当在终端中运行rev命令时,rev将接收用户在键盘上输入的字符串,进行反序后输出到屏幕,其原理如下图所示。
\
Figure 1.1: Every tool has three standard streams: standard input (stdin), standard output (stdout), and standard error (stderr)
我们还可以通过管道符号|将多个命令串联起来执行,前面一个命令的标准输出结果会传给后一个命令的标准输入进行进一步的处理。下面的命令下载一个文本文件,并通过grep搜索出其中含有CHAPTER的行:
$ curl -s "https://www.gutenberg.org/files/11/11-0.txt" | grep " CHAPTER"
CHAPTER I. Down the Rabbit-Hole
CHAPTER II. The Pool of Tears
CHAPTER III. A Caucus-Race and a Long Tale
CHAPTER IV. The Rabbit Sends in a Little Bill
CHAPTER V. Advice from a Caterpillar
CHAPTER VI. Pig and Pepper
CHAPTER VII. A Mad Tea-Party
CHAPTER VIII. The Queen’s Croquet-Ground
CHAPTER IX. The Mock Turtle’s Story
CHAPTER X. The Lobster Quadrille
CHAPTER XI. Who Stole the Tarts?
CHAPTER XII. Alice’s Evidence
这个命令行的工作原理如下图所示,curl下载文件的内容通过标准输出成为grep的输入,grep执行查找后输出到屏幕上:\
Figure 1.2: The output from a tool can be piped to another tool
注意,如果curl执行出错,错误信息并不会传给grep,而是显示在屏幕上。
我们还可以将命令行的输出进行重定向,比如重定向到文件中。下面的命令将执行结果重定向到chapter.txt文件中:
$ curl "https://www.gutenberg.org/files/11/11-0.txt" | grep " CHAPTER" > chapter
s.txt
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 170k 100 170k 0 0 197k 0 --:--:-- --:--:-- --:--:-- 198k
$ cat chapters.txt
CHAPTER I. Down the Rabbit-Hole
CHAPTER II. The Pool of Tears
CHAPTER III. A Caucus-Race and a Long Tale
CHAPTER IV. The Rabbit Sends in a Little Bill
CHAPTER V. Advice from a Caterpillar
CHAPTER VI. Pig and Pepper
CHAPTER VII. A Mad Tea-Party
CHAPTER VIII. The Queen’s Croquet-Ground
CHAPTER IX. The Mock Turtle’s Story
CHAPTER X. The Lobster Quadrille
CHAPTER XI. Who Stole the Tarts?
CHAPTER XII. Alice’s Evidence
下图是上面这个命令行的工作原理:
Figure 1.3: The output from a tool can be redirected to a file
在Linux系统中有一个特殊的文件 /dev/null,我们可以通过将错误输出重定向到该文件的方式禁止命令在屏幕上显示错误信息,如下所示:
$ cat movies.txt 404.txt
Matrix
Star Wars
Home Alone
Indiana Jones
Back to the Future
/usr/bin/cat: 404.txt: No such file or directory
$ cat movies.txt 404.txt 2> /dev/null ➊
Matrix
Star Wars
Home Alone
Indiana Jones
Back to the Future
其中数字2代表的是标准错误输出stderr。\
2. 开发环境准备
该书的作者提供了一个Docker镜像用于开发者学习使用。Docker的安装可以参考网上的教程,这里不再赘述。安装完Docker后,首先下载作者的镜像:
$ docker pull datasciencetoolbox/dsatcl2e
运行镜像:
$ docker run --rm -it datasciencetoolbox/dsatcl2e
Docker容器相当于一个沙箱,我们没有办法直接访问里面的文件,容器内也无法访问外部的文件,但是我们可以将本地的一个目录映射到容器内部,这样就可以在容器内部访问本地的文件。通过在运行容器的时候指定-v参数即可实现该功能,下面的命令将当前执行命令的目录映射到docker容器到根目录/data下面:
$ docker run --rm -it -v "$(pwd)":/data datasciencetoolbox/dsatcl2e
这样我们在容器内部就可以在/data目录下对外部的数据进行操作。
作者还提供了用于训练的测试数据,下载地址是:
www.datascienceatthecommandline.com/2e/data.zip
3. 获取数据
我们可以直接从网上下载数据,然后根据上一节的方法映在docker容器中进行处理,也可以在docker中直接从网上下载数据。Linux系统中有一个非常强大的工具curl,支持超过20多种网络协议,可以用来执行各种网络请求。
下载并保存文件
$ curl -s "https://en.wikipedia.org/wiki/List_of_windmills_in_Friesland" -O
或者
$ curl -s "https://en.wikipedia.org/wiki/List_of_windmills_in_Friesland" > friesland.html
-s的作用是抑制输出错误信息,-O将文件保存到本地。\
Http请求重定向
$ curl -s "https://youtu.be/dQw4w9WgXcQ"
-L参数用于指示curl处理需要重定向到网址。
$ curl -sI "https://youtu.be/dQw4w9WgXcQ" | trim
-I参数指示curl只返回http请求到header数据。
文件解压缩
$ tar -xzf logs.tar.gz ➊
如果只想看看压缩文件里有哪些内容,使用-t参数\
$ tar -tzf logs.tar.gz | trim
-C参数指定解压缩到特定目录下\
$ mkdir logs
$ tar -xzf logs.tar.gz -C logs
作者提供了一个简易的命令unpack,
$ unpack logs.tar.gz
Excel处理
CSVKit提供了一系列工具,如in2csv、csvgrep、csvlook等来处理csv文件。
in2csv这个命令可以将Excel文件转为csv文件
$ in2csv top2000.xlsx | tee top2000.csv | trim
要查看csv文件的内容,可以用下面的工具\
$ csvlook tmnt-with-header.csv
│ name │ nickname │ mask_color │ weapon │
├──────────────┼───────────────┼────────────┼──────────────────┤
│ Leonardo │ Leo │ blue │ two ninjakens │
│ Raphael │ Raph │ red │ pair of sai │
│ Michelangelo │ Mikey or Mike │ orange │ pair of nunchaku │
│ Donatello │ Donnie or Don │ purple │ staff │
这个工具将在终端中以格式化的方式显示结果。
搜索csv文件中特定的字符串:
$ csvgrep top2000.csv --columns ARTIEST --regex '^Queen$' | csvlook -I ➊
│ NR. │ ARTIEST │ TITEL │ JAAR │
├──────┼─────────┼─────────────────────────────────┼──────┤
│ 2 │ Queen │ Bohemian Rhapsody │ 1975 │
│ 11 │ Queen │ Love Of My Life │ 1975 │
│ 46 │ Queen │ Innuendo │ 1991 │
│ 55 │ Queen │ Don't Stop Me Now │ 1979 │
│ 70 │ Queen │ Somebody To Love │ 1976 │
│ 85 │ Queen │ Who Wants To Live Forever │ 1986 │
│ 89 │ Queen │ The Show Must Go On │ 1991 │
│ 131 │ Queen │ Killer Queen │ 1974 │
… with 24 more lines
--regex指定一个正则表达式。
数据库
在命令行中处理数据库等工具有很多,其中sql2csv,也是CSVkit中的一个工具,可以处理包括Oracle、MySQL等各种数据库,
$ sql2csv --db 'sqlite:///r-datasets.db' \
> --query 'SELECT row_names AS car, mpg FROM mtcars ORDER BY mpg' | csvlook
│ car │ mpg │
├─────────────────────┼──────┤
│ Cadillac Fleetwood │ 10.4 │
│ Lincoln Continental │ 10.4 │
│ Camaro Z28 │ 13.3 │
│ Duster 360 │ 14.3 │
│ Chrysler Imperial │ 14.7 │
│ Maserati Bora │ 15.0 │
│ Merc 450SLC │ 15.2 │
│ AMC Javelin │ 15.2 │
… with 24 more lines
Web API数据
这里也是用curl请求api数据
$ curl -s "https://anapioficeandfire.com/api/characters/583" | jq '.'
{
"url": "https://anapioficeandfire.com/api/characters/583",
"name": "Jon Snow",
"gender": "Male",
"culture": "Northmen",
"born": "In 283 AC",
"died": "", ➊
"titles": [
"Lord Commander of the Night's Watch"
],
curl也支持需要认证的http请求,可以在header中传入token等认证参数,这里将apikey保存到文件中,传给curl进行认证请求:
$ curl -s "http://newsapi.org/v2/everything?q=linux&apiKey=$(< /data/.secret/new
sapi.org_apikey)" |
> jq '.' | trim 30
{
"status": "ok",
"totalResults": 9088,
"articles": [
{
对数据流进行采样
$ curl -s "https://stream.wikimedia.org/v2/stream/recentchange" |
> sample -s 10 > wikimedia-stream-sample
4. 创建复杂的脚本
各种命令组合在一起可以完成非常复杂的任务,如下
$ curl -sL "https://www.gutenberg.org/files/11/11-0.txt" | ➊
> tr '[:upper:]' '[:lower:]' | ➋
> grep -oE "[a-z']{2,}" | ➌
> sort | ➍
> uniq -c | ➎
> sort -nr | ➏
> head -n 10 ➐
1839 the
942 and
811 to
638 of
610 it
553 she
486 you
462 said
435 in
403 alice
➊ curl下载一本电子书.
➋ tr将所有的文本转为小写.
➌ grep提取出所有的单词,每个单词放到单独的一行中.
➍ sort按字母顺序排序.
➎ uniq删除重复的词,并计算每个词出现的频率.
➏ sort按出现频率降序排序
➐ head保留前10个结果
上面这些词在英文中被称为stopwords,它们出现的频率本身就很高,为了更好的统计单词,最好过滤掉这些词不予统计。先通过下面的命令找出所有的stopwords:
$ curl -sL "https://raw.githubusercontent.com/stopwords-iso/stopwords-en/master/
stopwords-en.txt" |
> sort | tee stopwords | trim 20
10
39
a
able
ableabout
about
above
abroad
abst
accordance
according
accordingly
across
act
actually
ad
added
adj
adopted
ae
… with 1278 more lines
再使用下面的命令,统计除stopwords之外的单词:
$ curl -sL "https://www.gutenberg.org/files/11/11-0.txt" |
> tr '[:upper:]' '[:lower:]' |
> grep -oE "[a-z']{2,}" |
> sort |
> grep -Fvwf stopwords | ➊
> uniq -c |
> sort -nr |
> head -n 10
403 alice
98 gutenberg
88 project
76 queen
71 time
63 king
60 turtle
57 mock
56 hatter
55 gryphon
我们可以将上面的命令封装到一个脚本文件中,如下所示
$ bat top-words-5.sh
───────┬────────────────────────────────────────────────────────────────────────
│ File: top-words-5.sh
───────┼────────────────────────────────────────────────────────────────────────
1 │ #!/usr/bin/env bash
2 │
3 │ NUM_WORDS="${1:-10}"
4 │
5 │ tr '[:upper:]' '[:lower:]' |
6 │ grep -oE "[a-z']{2,}" |
7 │ sort |
8 │ grep -Fvwf stopwords |
9 │ uniq -c |
10 │ sort -nr |
11 │ head -n "${NUM_WORDS}"
───────┴────────────────────────────────────────────────────────────────────────
NUM_WORDS是从命令行传给脚本的参数, $1表示命令行的第一个参数,如果没有传参数取默认值“10”。
$ curl -sL "https://www.gutenberg.org/files/11/11-0.txt" > alice.txt
$ < alice.txt ./top-words-5.sh 20
403 alice
98 gutenberg
88 project
76 queen
71 time
\