《Data Science at the Command Line》阅读笔记。 用命令行工具来提升效率,还有这种操作.jpg
前言
对于习惯了 Windows 图形化操作界面的人来说,对于命令行 / 终端操作界面可能嗤之以鼻。但不可否认的是使用命令行的效率是大于图形界面的。 无意间看到了这本书,觉得里面的一些命令行操作非常实用,也是之前没有接触过的。故记录书中的一些通用的技巧,在很多 Linux 下面都是同样适用的。 对于涉及到具体的问题,可以诸如 Python 解决的,就跳过了。这些命令不必烂熟于行,但至少
本书在线阅读的网站是 https://www.datascienceatthecommandline.com/ 示例和 GitHub 地址是 https://github.com/jeroenjanssens/data-science-at-the-command-line
入门起步
首先你需要一定的 GNU/Linux 知识,至少会使用 Linux 系统。那么命令行工具的环境大致可以分为四层:命令(Command Line
)->终端(Terminal)
->Shell
->操作系统(Operating System)
。Shell 是解释你输入命令的,一般我们在 Linux 发行版中使用的 Shell 是 bash,当然也有其它的比如大名鼎鼎的 zsh。
如何执行命令行工具呢?在终端下输入相应的命令就行了,比如 $ pwd
就会给出当前的地址。
常见的命令行工具可以分成 5 种:
- 二进制可执行文件,一般是由其它编译而成的
- Shell 自带命令,比如
cd
和help
- 可解释脚本,如 Python 脚本
- Shell 函数,Shell 是自带了一些函数的如
seq
、fac
- 命令别名 (alias),你可以把一些很长的命令用别名的方式来简化。
结合使用不同的命令行工具,比如
|
|
seq 100
的作用是输出 1-100 的数,那么 grep 就在输出中找出含有 3
的项,之后 wc -l
就是数 grep
之后输出的行数,那么这里有 19 个。
重定向输入输出,如 $ seq 10 > output.txt
就可以把命令行的输出保存到文件中,这在许多调试中是非常有用的。
下面的命令中,echo的 -n
是不换行,>>
是指在文件后接着写 (append 的方式)
|
|
接下来看看怎么读取文件,一个常用的命令是 cat
,如 $ cat hello-world.txt | wc -w
,其中 -w
参数是数单词数 (words),这条的结果应该是 2,因为 "Hello World" 有两个单词。
当然还有其它的方法,这句和上面的输出是一样的 $ < hello-world.txt wc -w
,只不过它将 hello-word.txt 文件直接加载到了 wc 的输入流里面。或者直接使用 wc
的参数 $ wc -w hello-world.txt
,但此时输出会将文件名附在单词数后面。
和文件打交道,常用的命令是移动/剪切 mv
,复制 cp
,删除 rm
。下面整理了最常用的一些命令。
$ mv hello.txt ~/book/ch02/data/
将文件移动到文件夹中;
$ cd data
切换目录,$ mv hello.txt bye.txt
重命名;
$ rm bye.txt
删除文件,$ rm -r book/ch02/data/old
删除文件夹;
$ cp server.log server.log.bak
复制文件,$ mkdir logs
新建文件夹
在命令行中寻求帮助,对于每个命令行工具,一般是有相应的说明文档的,比如 man cat
就可以查看 cat
工具的说明。下面列举了常见的三种查看帮助文档的方式。
$ man cat | head -n 20
使用 man
命令查看帮助前 20 行;
$ help cd | head -n 20
对于一些工具使用 help
也是一样的。
$ jq --help
大部分工具都有 --help
这个参数,也一样可以查看帮助
Bash 常用快捷键,熟悉了的话能够提高不少效率。
获取数据
解压
在 Linux 中,我们最常见到的压缩文件后缀名是 .tar.gz
、.zip
和 .rar
文件,对应的解压缩命令是 tar
、unzip
和unrar
,注意在使用他们之前要确保已经正确安装了这些工具。
比如,$ tar -xzvf data/logs.tar.gz
,注意命令的参数是-xzvf
,分别对应解压 archive、gzip 解压、verbose 输出、指定 file。
书中提供了实用的 Bash 脚本来解压不同类型的文件,附上地址。
转换 Excel 文件
很多情况下,使用 Excel 保存的数据文件会存放在一个 xlsx
文件中,对于命令行工具来说非常不友好,通用一点的数据格式是 CSV 格式。
这时候 in2csv
命令就能派上用场了。需要先安装一下 sudo pip install csvkit
。
之后就可以使用命令 $ in2csv data/imdb-250.xlsx > data/imdb-250.csv
将xlsx文件转换成csv文件了。
使用 $ in2csv imdb-250.xlsx | head | cut -c1-80
可以查看,但是输出的是原始的逗号分隔文件,非常不友好。为了使显示效果更好,可以使用 csvlook
的管道。
如 in2csv data/imdb-250.xlsx | head | csvcut -c Title,Year,Rating | csvlook
的效果会好很多。
另一种处理的方式就是使用 Libre Office 这样的的软件打开,再进行转换。但这样非常不方便,而且你在用服务器的时候使不可能装一个 Office 套件的。
查询关系数据库
关系型数据库常见的有 MySQL、PostgreSQL 和 SQLite 等。使用命令行工具 sql2csv
可以更方便的使用 CSV 处理 SQL 数据库。
如使用 $ sql2csv --db 'sqlite:///data/iris.db' --query 'SELECT * FROM iris '\> 'WHERE sepal_length > 7.5'
进行 query 查询操作。
从网上下载数据
使用最多的是 cURL
工具。
$ curl -s http://www.gutenberg.org/cache/epub/76/pg76.txt | head -n 10
,参数 -s
表示 silent 模式。
$ curl http://www.gutenberg.org/cache/epub/76/pg76.txt > data/finn.txt
将 curl 下载的内容到文件中,此时不能用 -s
模式。
$ curl -s http://www.gutenberg.org/cache/epub/76/pg76.txt -o data/finn.txt
使用 -o
参数指定输出文件,此时可以使用 -s
。
$ curl -u username:password ftp://host/file
下载 FTP 上的文件
$ curl -L j.mp/locatbbar
当网址是诸如 http://bit.ly/*
的自动跳转等短网址时,加上 -L
参数就可以下载跳转之后的网页内容。
通过API请求数据
很多时候我们需要通过一些在线服务提供的 API 来获取数据。比如我们需要知道某地的天气时,就需要通过一些天气服务提供商提供的网络接口来获取数据,通常返回的数据格式时 json 形式的。使用最基础的 curl 就能实现,如$ curl -s http://api.randomuser.me | jq '.'
当我们需要更高级的操作时则需要其它工具,这在后续会涉及到。
创建可复用的命令行工具
其实就是将命令写进一个脚本文件。这在我们需要重复连续执行繁琐的命令的时候会非常有用。当然这里只能介绍一些基本的脚本编写,更复杂的需要阅读 Shell 脚本编写的相关书籍。
将单行指令转换为Shell脚本
通常要经历下面几个步骤:
- 将单行指令复制粘贴到一个文件中
- 给文件添加执行权限
- 定义一个 shebang 行(如
#!/bin/sh
) - 去除固定输入部分
- 添加参数
- 可选的拓展你的路径
复制单行命令到文件
有一个技巧是可以通过 !!
来代替上一条执行了的命令。
一般来说我们会将 Bash 脚本命令放到 .sh
文件中,表明是 shell 脚本。我们就可以通过 $ bash book/ch04/top-words-1.sh
这样的命令来执行 Shell 脚本了。
添加权限
绝大多数情况下,我们使用 vim
或者 nano
编辑器创建的 .sh
文件是没法直接执行的。因为我们还需要给它赋予可执行权限。那么这时候就要用到更改权限的命令 chmod
。在终端中执行 $ chmod u+x top-words-2.sh
,其中 u
指定是用户(也就是你),+x
代表增加执行权限。这样你就可以在终端中直接执行 ./top-words-2.sh
来运行这个脚本了。
通过 $ ls -l top-words-2.sh
来查看文件具体信息,我们可以看到权限部分由原来的 -rw-rw-r--
变到了 -rwxrw-r--
,增加了 x,执行权限。
定义 shebang
在 bash 脚本的第一行,我们需要加入 #!/usr/bin/env bash
来告诉终端是用bash来执行这个脚本。
如果你添加的是 #!/usr/bin/python
,那么就是用 Python 来执行。
去除固定输入
把 bash 脚本中的固定输入部分删除,这样我们就可以通过终端把我们需要的输入直接给脚本,而无须每次都改动脚本。
$ cat data/finn.txt | top-words-4.sh
,通过管道把数据给 shell 脚本。
参数化
下面的脚本内容定义了一个变量 NUM_WORDS
,在使用这个变量的时候需要在前面加$符号,这个变量的内容是 $1
代表着脚本第一个输入参数,比如 ./xx.sh 1
里的 1。
|
|
拓展你的 PATH
很多情况下,在不同的文件夹中,你也想使用你之前编写好的脚本,这时候把脚本所在路径添加到系统PATH中就能够使你在任何地方访问到。
$ echo $PATH | fold
想要长期的改变系统的环境变量的话,就需要修改 .bashrc
或 .profile
文件了。
使用 Python 创建命令行工具
迁移脚本
上面的脚本,可以同样使用Python来实现,下面代码给出了实现。
|
|
这时候我们就可以通过 Python 脚本来执行我们所需的功能,将数据流输入到脚本并附上参数 5。$ < data/76.txt top-words.py 5
使用标准输入来处理流数据
当输入数据流是类似命令行形式而非文件时,你就不可能一次性全部处理掉,故需要一行一行处理。
|
|
清洗数据
对普通文本操作
过滤行
主要用到的命令行工具是 sed
和 awk
基于位置:
生成一个 10 行的数据用于演示。$ seq -f "Line %g" 10 | tee data/lines
打印前三行:$ < lines head -n 3
;$ < lines sed -n '1,3p'
;$ < lines awk 'NR<=3'
打印末三行:$ < lines tail -n 3
删除前三行:$ < lines tail -n +4
;$ < lines sed '1,3d'
;$ < lines sed -n '1,3!p'
删除末三行:$ < lines head -n -3
打印 4-6 行:$ < lines sed -n '4,6p'
;$ < lines awk '(NR>=4)&&(NR<=6)'
;$ < lines head -n 6 | tail -n 3
打印奇数行:$ < lines sed -n '1~2p'
;$ < lines awk 'NR%2'
打印偶数行:$ < lines sed -n '0~2p'
;$ < lines awk '(NR+1)%2'
基于模式:
使用 grep
和正则表达式
$ grep -E '^CHAPTER (.*)\. The' alice.txt
,在 alice.txt 中找出所有以 "The" 开头的章节
基于随机:
使用 sample
工具创建数据集的子集
$ seq 1000 | sample -r 1% | jq -c '{line: .}'
可以指定 sample 的延时,避免数据一下子太快打印出来而产生错误,这在调试你的脚本时非常有用。
$ seq 10000 | sample -r 1% -d 1000 -s 5 | jq -c '{line: .}'
提取值
使用 grep
的输出到 cut
工具中
$ grep -i chapter alice.txt | cut -d' ' -f3-
替换与删除
使用 tr
工具
$ echo 'hello world!' | tr ' ' '_'
,把空格替换成了下划线
$ echo 'hello world!' | tr -d -c '[a-z]'
,删除不是 a-z 的字符
tr
也可以用来转换大小写
$ echo 'hello world!' | tr '[a-z]' '[A-Z]'
,将小写转换成大写
$ echo 'hello world!' | tr '[:lower:]' '[:upper:]'
,与上面效果相同
操作 CSV
使用了三种工具:body
、header
和 cols
操作 HTML 和 JSON
使用了curl
、scrape
、xml2json
、jq
、json2csv
管理你的数据工作流
原书使用了 Drake 工具。因为不太通用,故跳过。
探索数据
用了 R 语言中大名鼎鼎的 ggplot2 来进行数据可视化。 在 Python 中可以使用 matplotlib 或 plotly 等可视化工具。
并行管道
普通循环
这个还是挺有用的。
使用 bc
来计算数学表达式。$ echo "4^2" | bc
|
|
逐行循环:
|
|
也可以从标准输入流(即你的控制台输入)中获取数据
$ while read i; do echo "You typed: $i."; done < /dev/stdin
逐个文件循环:
|
|
还可以使用 find
来更灵活的寻找文件
$ find data -name '*.csv' -exec echo "Processing {}" \;
GNU Parallel
非常好用的一个工具,可以让循环变得简单,并且易于管理。
$ seq 5 | parallel "echo {}^2 | bc"
对数据建模
降维、聚类、回归、分类,机器学习问题。这里不展开了。 可以利用 python 等更友好的实现这些。