命令行数据科学工具笔记

《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 自带命令,比如 cdhelp
  • 可解释脚本,如 Python 脚本
  • Shell 函数,Shell 是自带了一些函数的如 seqfac
  • 命令别名 (alias),你可以把一些很长的命令用别名的方式来简化。

结合使用不同的命令行工具,比如

1
$ seq 100 | grep 3 | wc -l

seq 100 的作用是输出 1-100 的数,那么 grep 就在输出中找出含有 3 的项,之后 wc -l 就是数 grep 之后输出的行数,那么这里有 19 个。

重定向输入输出,如 $ seq 10 > output.txt 就可以把命令行的输出保存到文件中,这在许多调试中是非常有用的。 下面的命令中,echo的 -n 是不换行,>> 是指在文件后接着写 (append 的方式)

1
2
$ echo -n "Hello" > hello-world.txt
$ echo " World" >> hello-world.txt

接下来看看怎么读取文件,一个常用的命令是 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 文件,对应的解压缩命令是 tarunzipunrar,注意在使用他们之前要确保已经正确安装了这些工具。 比如,$ 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脚本

通常要经历下面几个步骤:

  1. 将单行指令复制粘贴到一个文件中
  2. 给文件添加执行权限
  3. 定义一个 shebang 行(如 #!/bin/sh)
  4. 去除固定输入部分
  5. 添加参数
  6. 可选的拓展你的路径

复制单行命令到文件

有一个技巧是可以通过 !! 来代替上一条执行了的命令。 一般来说我们会将 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。

1
2
3
4
#!/usr/bin/env bash
NUM_WORDS="$1"                                        
tr '[:upper:]' '[:lower:]' | grep -oE '\w+' | sort |
uniq -c | sort -nr | head -n $NUM_WORDS    

拓展你的 PATH

很多情况下,在不同的文件夹中,你也想使用你之前编写好的脚本,这时候把脚本所在路径添加到系统PATH中就能够使你在任何地方访问到。 $ echo $PATH | fold 想要长期的改变系统的环境变量的话,就需要修改 .bashrc.profile 文件了。

使用 Python 创建命令行工具

迁移脚本

上面的脚本,可以同样使用Python来实现,下面代码给出了实现。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/usr/bin/env python
import re
import sys
from collections import Counter
num_words = int(sys.argv[1])
text = sys.stdin.read().lower()
words = re.split('\W+', text)
cnt = Counter(words)
for word, count in cnt.most_common(num_words):
    print "%7d %s" % (count, word)

这时候我们就可以通过 Python 脚本来执行我们所需的功能,将数据流输入到脚本并附上参数 5。$ < data/76.txt top-words.py 5

使用标准输入来处理流数据

当输入数据流是类似命令行形式而非文件时,你就不可能一次性全部处理掉,故需要一行一行处理。

1
2
3
4
5
6
7
8
#!/usr/bin/env python
from sys import stdin, stdout
while True:
    line = stdin.readline()
    if not line:
        break
    stdout.write("%d\n" % int(line)**2)
    stdout.flush()

清洗数据

对普通文本操作

过滤行

主要用到的命令行工具是 sedawk

基于位置: 生成一个 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

使用了三种工具:bodyheadercols

操作 HTML 和 JSON

使用了curlscrapexml2jsonjqjson2csv

管理你的数据工作流

原书使用了 Drake 工具。因为不太通用,故跳过。

探索数据

用了 R 语言中大名鼎鼎的 ggplot2 来进行数据可视化。 在 Python 中可以使用 matplotlib 或 plotly 等可视化工具。

并行管道

普通循环

这个还是挺有用的。 使用 bc 来计算数学表达式。$ echo "4^2" | bc

1
2
3
4
$ for i in {01002}  
> do
> echo "$i^2" | bc      
> done | tail    

逐行循环:

1
2
3
4
$ while read line                                       
> do
> echo "Sending invitation to ${line}."                 
> done < data/emails.txt   

也可以从标准输入流(即你的控制台输入)中获取数据 $ while read i; do echo "You typed: $i."; done < /dev/stdin

逐个文件循环:

1
2
3
4
$ for filename in *.csv
> do
> echo "Processing ${filename}."
> done

还可以使用 find 来更灵活的寻找文件 $ find data -name '*.csv' -exec echo "Processing {}" \;

GNU Parallel

非常好用的一个工具,可以让循环变得简单,并且易于管理。 $ seq 5 | parallel "echo {}^2 | bc"

对数据建模

降维、聚类、回归、分类,机器学习问题。这里不展开了。 可以利用 python 等更友好的实现这些。

加载评论