建设一个怎样的自己的网站,如何网站建设,西安庆典公司,下载赶集网招聘最新招聘这章也是第一版没有#xff0c;第二版新增的。 3. 探索性数据分析
上一章给出了一些个人使用的小脚本#xff0c;通常是特制或专用的。在本章中#xff0c;我们还会展示Awk在现实中的典型使用场景#xff1a;使用Awk和其他工具来非正式地探索一些真实的数据#xff0c;目… 这章也是第一版没有第二版新增的。 3. 探索性数据分析
上一章给出了一些个人使用的小脚本通常是特制或专用的。在本章中我们还会展示Awk在现实中的典型使用场景使用Awk和其他工具来非正式地探索一些真实的数据目的是为了看看它们是什么样的。这叫做探索性数据分析exploratory data analysis 或 EDA该术语由统计学先驱John Tukey 首次使用。
Tukey 发明了大量基本的数据化可视技术如箱线图boxplot启发了统计编程语言S即广泛使用的R语言的前身合作发明了快速傅里叶变换并创造了 bit 和 software 这两个词。作为朋友和同事作者们在上世纪80和90年代期间与 Tukey 相识于贝尔实验室在这许多非常聪明且极富创造力的人之中他也是相当突出而特别的。
探索性数据分析的本质是在提出假设和给出结论之前先玩转数据。如Tukey本人所说“找出问题通常比发现答案更重要探索性数据分析是态度是灵活性是对展示的依赖而不是一堆技术。”
在很多情况下这需要涉及计数简单的统计用不同方式排列数据寻找模式、共同点和奇异值并绘制基本的图表等可视化内容。重点在于通过做一些小而快的实验以期能得出一些有用的启示。注意一开始不需要精雕细琢那可以在我们对数据有了更好的认识之后再来做。
对于EDA我们通常使用标准Unix工具如 wc、diff、 sort、uniq、grep当然还有正则表达式。这些工具与Awk结合在一起使用效果很好也经常结合其他语言如Python一起使用。
下面我们将遇到好几种文件格式包括逗号或制表符分隔的值CSV和TSVJSON、HTML和XML。其中一些如CSV和TSV很容易用Awk处理而其他类型有时用其他工具处理更好。 3.1 泰坦尼克号的沉没
第一个数据集是基于1912年4月15日的泰坦尼克号沉没事件。本书的作者之一会选择这个例子不完全是出于巧合当时他正经历跨大西洋的航行在泰坦尼克沉没点的不远处经过。
汇总数据titanic.tsv
titanic.tsv 文件内容来源于维基百科包含了泰坦尼克号乘客和船员的汇总数据。对CSV和TSV格式来说通常第一行会是表头用来标识后面的数据。列之间用制表符tab分隔。
Type Class Total Lived Died
Male First 175 57 118
Male Second 168 14 154
Male Third 462 75 387
Male Crew 885 192 693
Female First 144 140 4
Female Second 93 80 13
Female Third 165 76 89
Female Crew 23 20 3
Child First 6 5 1
Child Second 24 24 0
Child Third 79 27 52
许多也许是所有的数据集都包含错误。这里可以做个快速检查每行要有5个域第3个域要等于第4和第5个域的和total lived died。下面的程序打印出所有不符合条件的行
NF ! 5 || $3 ! $4 $5
如果数据格式正确而且数值也正确只会打出一行即表头
Type Class Total Lived Died
一旦完成这些最简单的检查后我们就能看看其他东西了。比如每个类别的人有多少
这些类别不是用数字而是用单词标识的如Male和Crew。幸运的是Awk数组的下标或者说索引可以是任意的字符串因此 gender[Male] 和 class[Crew] 都是合法的表达式。
允许用任意字符串做下标的数组称为关联数组其他语言也提供相同的机制称之为字典、映射或者哈希映射。关联数组特别地方便、灵活因此我们将会大量地用到它。
NR 1 { gender[$1] $3; class[$2] $3 }END {for (i in gender) print i, gender[i]print for (i in class) print i, class[i]
}
上面的程序会输出
Male 1690
Child 109
Female 425Crew 908
First 325
Third 706
Second 285
Awk的 for 语句有个特殊形式可用于遍历关联数组的索引
for (i in array) { statements }
会将变量 i 依次设置为数组的索引并在执行 statements 时使用对应 i 即当前索引的值 。数组元素的访问顺序是不确定的你不能依赖于任何特定的顺序。【注上面的例子用awk和gawk得到的结果顺序就不一样】
下面来看看生存率是怎么样的。社会阶层、姓名和年龄会如何影响这些乘客的生存机会我们可以用这些汇总数据来做个简单的实验例如计算每类人的生存率
NR 1 { printf(%6s %6s %6.1f%%\n, $1, $2, 100 * $4/$3) }
可以将awk的输出通过管道传给Unix 命令 sort -k3 -nr按第三列倒序排序得到结果 Child Second 100.0%
Female First 97.2%
Female Crew 87.0%
Female Second 86.0%Child First 83.3%
Female Third 46.1%Child Third 34.2%Male First 32.6%Male Crew 21.7%Male Third 16.2%Male Second 8.3%
显然妇女和儿童的生存率高于平均水平。
注意上面的例子都将表头行当作特例进行排除用NR 1。如果你要做很多实验那么把表头从数据集中删去也许会比每次写代码去忽略首行要简单。
乘客数据passengers.csv
passengers.csv 文件较大它包含了乘客的详细信息但没有包含船员信息。原始文件是把一个广泛使用的机器学习数据集和另一个维基百科的列表合并起来得到的。文件共有11列其中包含了乘客们的故乡、所分配的救生艇、票价等
row.names,pclass,survived,name,age,embarked,home.dest,room,ticket,boat,sex
...
11,1st,0,Astor, Colonel John Jacob,47,Cherbourg,New York, NY,,17754 L224 10s 6d,(124),male
...
这文件有多大可以使用Unix的 wc 命令来统计行数、单词数和字符数
$ wc passengers.csv1314 6794 112466 passengers.csv
或者使用我们在第一章写的两行Awk程序来统计 { nc length($0) 1; nw NF }
END { print NR, nw, nc, FILENAME }
得到的结果是一样的当然两者在输出格式上稍微有区别比如空格数量不一样
passengers.csv 文件是 CSV格式comma-separated values。尽管CSV格式没有严格的定义但通常都有这样的规则如果某一域的内容包含逗号或者双引号则整个域都要用双引号包起来。任何域都可以用双引号包起来不管它的内容是否包含逗号或者双引号。空的域表示为两个双引号而域内容里的双引号用两个双引号表示例如 , 前后共6个双引号表示 ,。CSV文件的输入域可以包含换行符。更多细节参见附录 A.5.2。
这种格式大致也是Ms Excel和其他电子表格程序如Apple Numbers和Google Sheets所采用的。这种格式还是Python的Panda库和R语言默认的data frame格式。
2023年以来的Awk版本如果加入命令行参数 --csv 则会将输入行按上面的格式进行拆分。如果只是简单的用 FS, 来设置域分隔符则无法做到这一点这种做法只能处理最简单的CSV格式即不包含双引号的。如果你使用旧版本的Awk也许最简单的做法是用其他工具如Excel或Python的CSV模块把数据转换成其他格式。
另一个有用的替代格式是TSV制表符分隔的值。它和CSV思想是一样的但更简单域之间用单个tab制表符分隔没有双引号机制因此域就不能包含嵌套的tab或换行。这种格式用Awk处理很简单只要通过 FS\t 将域分隔符设置为tab或者在命令行参数中带 -F\t 效果也是一样的。
另外提一下在处理文件内容之前验证一下它的格式是否正确会比较明智。例如校验所有的记录行是否都有相同的域数量可以使用
awk {print NF} file | sort | uniq -c | sort -nr
其中第一个sort 排序命令能够将所有相同的行都聚在一起之后的 uniq -c 命令会将连续的相同行合并成一行给出相同行的计数以及该行的内容最后的 sort -nr 把结果按数值倒序排列因此最大值输出在最前面。
对passengers.csv文件用上面的脚本再额外加上 --csv 选项来对CSV格式进行正确处理得到
1314 11
可见每条记录的域数量都相同这是合法数据的必要条件当然不是充分条件。如果发现某些行的域数量不一样接着就可以使用Awk把它们找出来比如用 NF ! 11 条件来过滤。
如果使用老版本即不支持CSV的Awk用 -FS, 来处理文件会得到
624 12
517 13
155 1415 153 11
可见基本上所有的记录里都包含了内嵌的逗号。
顺带一提生成CSV是很容易的。下面这个函数 to_csv 通过将双引号替换成两个双引号并在得到的结果两边加上双引号就能把字符串转换成符合CSV格式要求的字符串。这个函数也可以添加到第二章所述的个人库里面。
# to_csv - convert s to proper ...function to_csv(s) {gsub(//, \\, s)return \ s \
}
注意在字符串中引号使用反斜杠进行转义处理
我们可以在循环中使用这个函数并在数组的元素之间插入逗号来将一个数组关联数组或索引数组转换成一条格式合法的CSV记录。参见如下两个函数 rec_to_csv 和 arr_to_csv
# rec_to_csv - convert a record to csvfunction rec_to_csv( s, i) {for (i 1; i NF; i)s s to_csv($i) ,s s to_csv($NF)return s
}# arr_to_csv - convert an indexed array to csvfunction arr_to_csv(arr, s, i, n) {n length(arr)for (i 1; i n; i)s s to_csv(arr[i]) ,return substr(s, 1, length(s)-1) # remove trailing comma
}
下面的程序从原始文件中选择五个属性类别、是否生还、姓名、年龄、性别输出域之间用制表符tab分隔
NR 1 { OFS\t; print $2, $3, $4, $5, $11 }
输出结果如下
1st 0 Allison, Miss Helen Loraine 2 female
1st 0 Allison, Mr Hudson Joshua Creighton 30 male
1st 0 Allison, Mrs Hudson J.C. (Bessie Waldo Daniels) 25 female
1st 1 Allison, Master Hudson Trevor 0.9167 male
年龄字段大部分是整数但也有部分是小数如上面的最后一行。Helen Allison是两岁 Master Hudson Allison看起来是11个月大而且是他们家族的唯一幸存者。从其他来源我们得知Allison家的司机George Swane18岁也遇难了但女佣和厨师都得救了
泰坦尼克上有多少婴儿执行命令
$4 1
并用tab作为域分隔符可以得到8行
1st 1 Allison, Master Hudson Trevor 0.9167 male
2nd 1 Caldwell, Master Alden Gates 0.8333 male
2nd 1 Richards, Master George Sidney 0.8333 male
3rd 1 Aks, Master Philip 0.8333 male
3rd 0 Danbom, Master Gilbert Sigvard Emanuel 0.3333 male
3rd 1 Dean, Miss Elizabeth Gladys (Millvena) 0.1667 female
3rd 0 Peacock, Master Alfred Edward 0.5833 male
3rd 0 Thomas, Master Assad Alexander 0.4167 male
练习3-1、修改单词计数程序使之能为每个输入文件产生一个单独的计数结果就像Unix命令wc一样。
更进一步的检查
另一类要探索的问题是两个数据源的一致性。两个数据都来自维基百科但它并不总是完全精确的数据源。假如我们检查一些最基本的比如有多少乘客在 passengers 文件中
$ awk END {print NR} passengers.csv
1314
这里还包含了表头行所以是1313个乘客。而我们还可以从汇总文件第三个域中统计非船员的人数
$ awk !/Crew/ { s $3 }; END { print s } titanic.tsv
1316
差了3个人肯定哪里错了。
再来个例子算算有多少儿童
awk --csv $5 12 passengers.csv
结果有100行和汇总文件titanic.tsv里得到的109人对不上。
也许儿童的定义是不超过13岁但结果是105。用14岁试试结果是112。通过计算被称为“Master”的乘客数量我们可以猜测用的是哪个数字
awk --csv /Master/ {print $5} passengers.csv | sort -n
里面最大的年龄是13岁尽管不能确定但也许这个猜测最接近实际。
上面两种情况下数字都应该是相同的但实际上却不同这说明数据还是不太靠谱。可见在探索数据时你要总是准备好应付数据在形式和内容上的错误和不一致。在开始下结论之前要做大量的工作来保证你已经识别并处理了潜在的问题。
在本节中我们已尽力展示如何用简单的计算来帮助识别这样的问题。如果你收集一些工具来做公共的操作如分离域按类分组打印最常见和最少见的条目等等你就更好地进行这些检查操作。
练习3-2、根据你自己的需要和品味为自己写一些工具。