前言
之前針對(duì)WorkerHub小程序做的數(shù)據(jù)分析文章 互聯(lián)網(wǎng)卷王花落誰(shuí)家? 收到了一些小伙伴的回復(fù)敢朱,點(diǎn)名要學(xué)習(xí)數(shù)據(jù)分析侦啸,其實(shí)我也是一知半解碑宴,想著來寫幾篇文章簡(jiǎn)單聊下我分析的過程梗肝。
首先是數(shù)據(jù)清洗和統(tǒng)計(jì)了,這塊我并沒有用諸如Python之類的腳本語(yǔ)言航背,雖然Python提供了很多強(qiáng)大的數(shù)據(jù)分析庫(kù)如Pandas喉悴、Numpy等,但是畢竟要麻煩一些玖媚,還要寫個(gè)腳本箕肃,裝一堆庫(kù)(PS:其實(shí)就是懶)。
我用的是一個(gè)老的Unix工具 AWK今魔,雖然歷史比較久遠(yuǎn)勺像,但是它簡(jiǎn)潔和豐富的功能可以稱之為神器,同時(shí)也是后臺(tái)同學(xué)必須要掌握的一個(gè)工具错森,畢竟通過日志緊急定位線上問題的時(shí)候吟宦,你不能跟老板說:等等我先寫個(gè)腳本,那老板原地暴斃了涩维。
應(yīng)用場(chǎng)景
AWK是1977年貝爾實(shí)驗(yàn)室的三個(gè)兄弟( Alfred Aho殃姓、Peter Weinberger、 Brian Kernighan )搞出來的文本分析工具瓦阐,這三個(gè)哥們的首字母拼起來就是AWK的名字了辰狡。
AWK處理文本就像其他語(yǔ)言處理數(shù)值一樣方便,所以經(jīng)常被應(yīng)用在文本處理領(lǐng)域垄分。
比如日志分析宛篇、數(shù)據(jù)清洗、文本過濾薄湿、數(shù)據(jù)統(tǒng)計(jì)等叫倍。
同時(shí)AWK也是一門編程語(yǔ)言,不過它的命令行用法就可以滿足大多數(shù)的應(yīng)用場(chǎng)景豺瘤。
我們通尺壕耄可以使用一行AWK命令完成一個(gè)腳本的任務(wù)!W蟆蚕泽!
AWK所適用的文本處理通常都有一些共同&顯著的特點(diǎn):
1. 輸入數(shù)據(jù)格式統(tǒng)一
比如日志,為了對(duì)日志進(jìn)行上報(bào)桥嗤、監(jiān)控须妻、統(tǒng)計(jì)分析,我們通常會(huì)采用一些分割手段來記錄日志 (或者json等易于統(tǒng)計(jì)的格式)泛领。
例如如下日志采用”|”來分割日志荒吏。
# 日志格式:{服務(wù)}|{日期}|{業(yè)務(wù)}|{請(qǐng)求URL}|{返回狀態(tài)}|{請(qǐng)求耗時(shí)}|{請(qǐng)求參數(shù)}|{返回參數(shù)}...
比如CSV文件,采用”,”來分割渊鞋。
# CSV格式:field1,field2,field3...
如果輸入數(shù)據(jù)不是固定格式绰更,通常會(huì)使用sed瞧挤、grep等工具來過濾、清洗為awk可以處理的形式儡湾。
2. 每一列代表固定含義特恬,便于數(shù)據(jù)分析
輸入文件每一行的相同列類型一致,如果每一列含義不同徐钠,那就失去了數(shù)據(jù)分析的意義癌刽。
比如本文的第一個(gè)演示數(shù)據(jù),第一列表示地區(qū)丹皱,第二列表示總?cè)丝诘取?/p>
演示數(shù)據(jù)來源于國(guó)家統(tǒng)計(jì)局妒穴。
各地區(qū)戶口登記地在外鄉(xiāng)鎮(zhèn)街道的人口狀況
由于演示數(shù)據(jù)文件行數(shù)太多占用篇幅較長(zhǎng)宋税,以下演示均只展示前幾條數(shù)據(jù)摊崭。
$ cat population.txt|head -n 10
地區(qū) 合計(jì) 本縣/市/區(qū) 本省其他縣/市/區(qū) 省外
全國(guó) 260937942 90372599 84689006 85876337
北京 10498288 1582574 1871181 7044533
天津 4952225 1095282 865442 2991501
河北 8297279 4263957 2628649 1404673
山西 6764665 3643627 2189385 931653
內(nèi)蒙古 7170889 2732591 2994117 1444181
遼寧 9310058 3899728 3623800 1786530
吉林 4462177 2604239 1401439 456499
黑龍江 5557828 2800727 2250704 506397
Let’s start !=苋呢簸!
基本用法
一個(gè)AWK程序的組成非常簡(jiǎn)單,它的核心內(nèi)容是:一個(gè)或多個(gè) “模式–動(dòng)作” 語(yǔ)句序列乏屯。
“模式–動(dòng)作” 序列用單引號(hào)包起來根时,動(dòng)作放在花括號(hào)里,再傳入輸入文件即可辰晕。
### 一個(gè) 模式-動(dòng)作
awk 'pattern {action}' input_files
### 多個(gè) 模式-動(dòng)作
awk 'pattern1 {action1} pattern2 {action2} pattern3 {action3} ...' input_files
AWK會(huì)每次讀取一個(gè)輸入行蛤迎,對(duì)讀取到的每一行,按順序檢查每一個(gè)模式含友。
如果當(dāng)前行符合模式替裆,則執(zhí)行對(duì)應(yīng)動(dòng)作。
所以AWK的工作原理就是按順序匹配模式然后執(zhí)行動(dòng)作窘问。
可以想象到AWK偽代碼大概長(zhǎng)這樣辆童,我猜的(_)。
### AWK偽代碼 我猜的 (*^_^*)
while(getline(inputfile))
{
if(模式1 == true)
{
動(dòng)作1;
}
if(模式2 == true)
{
動(dòng)作2;
}
....
}
AWK在自動(dòng)掃描輸入文件的同時(shí), 也會(huì)按照分隔符(默認(rèn)空格/Tab)把每一個(gè)輸入行切分成字段惠赫。
其中 $0 表示整行把鉴,$1,$2…$n 分別表示第一列,第二列…第N列儿咱。
大致的流程圖如下:
大部分的工作都是AWK自動(dòng)完成的:包括按行輸入庭砍,字段分割,字段存儲(chǔ)等混埠。
所以我們只需要給出 “模式–動(dòng)作” 序列就可以完成對(duì)文件的操作6和!岔冀!
來個(gè) Hello World 吧凯旭,輸出 “hello” 和 整行 ($0)概耻。
print 函數(shù)使用逗號(hào)分隔不同的參數(shù),打印結(jié)果用空格符分隔罐呼,并且會(huì)自動(dòng)換行鞠柄。(類似于各大語(yǔ)言println函數(shù))。
模式可以省略嫉柴,表示匹配所有行厌杜。
$ awk '{print "hello",$0}' population.txt|head -n 5
hello 地區(qū) 合計(jì) 本縣/市/區(qū) 本省其他縣/市/區(qū) 省外
hello 全國(guó) 260937942 90372599 84689006 85876337
hello 北京 10498288 1582574 1871181 7044533
hello 天津 4952225 1095282 865442 2991501
hello 河北 8297279 4263957 2628649 1404673
AWK提供了很多有用的內(nèi)置變量,如:
NR (Number Of Record) :表示讀取到的記錄數(shù)计螺,即當(dāng)前行號(hào)夯尽。
FILENAME :表示當(dāng)前輸入的文件名。
NF (Number Of Field) :表示當(dāng)前記錄的字段個(gè)數(shù)登馒,即總共多少列匙握。
我們通常用 $NF 提取當(dāng)前行的最后一列。
如下例子所示陈轿,總共有5列圈纺,5麦射,$(NF-1)表示倒數(shù)第二列的值蛾娶。
$ awk '{print FILENAME,NR,$1,$3,NF,$NF}' population.txt|head -n 5
population.txt 1 地區(qū) 本縣/市/區(qū) 5 省外
population.txt 2 全國(guó) 90372599 5 85876337
population.txt 3 北京 1582574 5 7044533
population.txt 4 天津 1095282 5 2991501
population.txt 5 河北 4263957 5 1404673
常見的內(nèi)建變量可以去附錄查閱:常見的內(nèi)建變量 。
AWK也提供了格式化輸出函數(shù)潜秋,跟C語(yǔ)言的printf用法一樣蛔琅。
$ awk '{printf "%s的外地總?cè)丝谟?%d,省外人口有:%0.2f\n",$1,$2,$NF}' population.txt|tail -n 5
陜西的外地總?cè)丝谟?5894416,省外人口有:974362.00
甘肅的外地總?cè)丝谟?3112722,省外人口有:432833.00
青海的外地總?cè)丝谟?1140954,省外人口有:318435.00
寧夏的外地總?cè)丝谟?1534482,省外人口有:368451.00
新疆的外地總?cè)丝谟?4276951,省外人口有:1791642.00
格式化規(guī)則可以參考:https://www.gnu.org/software/gawk/manual/html_node/Control-Letters.html 。
模式過濾
上面介紹了動(dòng)作的使用峻呛,動(dòng)作通常用來輸出展示罗售。
模式用來過濾我們想要的記錄。
如下篩選(行號(hào)>1 且 第二列大于11074525)的行杀饵。
### AWK的變量也可以自由進(jìn)行算術(shù)運(yùn)算(加減乘除)莽囤,比如 $2-$3
$ awk 'NR>1 && $2>11074525 {print NR,$1,$2,$2-$3}' population.txt
2 全國(guó) 260937942 170565343
11 上海 12685316 11016029
12 江蘇 18226819 13681789
13 浙江 19900863 15274032
17 山東 13698321 7123530
21 廣東 36806649 31390437
25 四川 11735152 6913850
AWK的字符串拼接跟shell一樣簡(jiǎn)單粗暴,不需要使用任何運(yùn)算符切距。
將兩個(gè)字符串并排放在一起就能實(shí)現(xiàn)拼接朽缎。
$ awk 'NR>1 {print NR,"開始_"$1"_結(jié)束"}' population.txt|head -n 5
2 開始_全國(guó)_結(jié)束
3 開始_北京_結(jié)束
4 開始_天津_結(jié)束
5 開始_河北_結(jié)束
6 開始_山西_結(jié)束
AWK還提供了很多有用的內(nèi)置函數(shù)。
length(s):用來計(jì)算字符串s 的長(zhǎng)度谜悟。
### 我的系統(tǒng)編碼 & 文件編碼均為UTF-8
$ awk 'length($1) > 6 {print $1,"占用長(zhǎng)度:",length($1)}' population.txt
內(nèi)蒙古 占用長(zhǎng)度: 9
黑龍江 占用長(zhǎng)度: 9
substr(s,p):求字符串s的子串话肖,從位置p開始到末尾。
$ awk '{print $1,substr($1,4)}' population.txt|head -n 5
地區(qū) 區(qū)
全國(guó) 國(guó)
北京 京
天津 津
河北 北
常見的內(nèi)建函數(shù)可以去附錄查閱:常見的內(nèi)建函數(shù) 葡幸。
AWK還提供了一些特殊的模式最筒,比如 BEGIN 和 END。這兩個(gè)模式不匹配任何輸入行蔚叨。
當(dāng) awk讀取數(shù)據(jù)前床蜘,BEGIN 的語(yǔ)句開始執(zhí)行辙培,通常用于初始化。
例如我們可以用BEGIN來給輸出打印一個(gè)表頭邢锯。
### 多個(gè) "模式-動(dòng)作" 并排寫就行扬蕊。
$ awk 'BEGIN{print "AREA TOTAL LOCAL OTHER OUTLAND"} NR>2{print}' population.txt|head -n 5
AREA TOTAL LOCAL OTHER OUTLAND
北京 10498288 1582574 1871181 7044533
天津 4952225 1095282 865442 2991501
河北 8297279 4263957 2628649 1404673
山西 6764665 3643627 2189385 931653
當(dāng)所有輸入行被處理完畢,END 的語(yǔ)句開始執(zhí)行丹擎。通常用來收尾尾抑。
例如我們可以統(tǒng)計(jì)一下第二列大于262005的省份,并在END進(jìn)行打印蒂培。
$ awk 'NR>2 && $2>262005{count += 1} END{print count"個(gè)大于262005的省份"}' population.txt
30個(gè)大于262005的省份
同一個(gè)動(dòng)作里的多個(gè)語(yǔ)句之間使用分號(hào)或者換行進(jìn)行分割再愈。
如下在BEGIN的動(dòng)作中先指定輸出分隔符,接著打印表頭护戳。
OFS (Output Formmat Separate) 也是一個(gè)內(nèi)建變量:指定輸出字段分割符翎冲。
如下指定輸出時(shí)字段采用逗號(hào)進(jìn)行分割。
$ awk 'BEGIN{OFS=",";print "AREA,TOTAL,LOCAL,OTHER,OUTLAND"} NR>2{print $1,$2,$3,$4,$5}' population.txt|head -n 5
AREA,TOTAL,LOCAL,OTHER,OUTLAND
北京,10498288,1582574,1871181,7044533
天津,4952225,1095282,865442,2991501
河北,8297279,4263957,2628649,1404673
山西,6764665,3643627,2189385,931653
AWK提供了范圍模式可以根據(jù)一個(gè)區(qū)間來匹配多個(gè)輸入行灸异。
范圍模式由兩個(gè)被逗號(hào)分開的模式組成府适。
awk 'pattern1,pattern2 {action}' input_file
AWK從符合 pattern1 的行開始羔飞,到符合 pattern2 的行結(jié)束 (包括這兩行)肺樟,對(duì)這其中的每一行執(zhí)行action。
如下提取第五行到第十行之間地區(qū)的數(shù)據(jù)逻淌。
$ awk 'NR==5,NR==10" {print NR,$0}' population.txt
5 河北 8297279 4263957 2628649 1404673
6 山西 6764665 3643627 2189385 931653
7 內(nèi)蒙古 7170889 2732591 2994117 1444181
8 遼寧 9310058 3899728 3623800 1786530
9 吉林 4462177 2604239 1401439 456499
10 黑龍江 5557828 2800727 2250704 506397
流程控制
前文提到了AWK也是一門編程語(yǔ)言么伯,所以它支持很多編程語(yǔ)言特性,與C語(yǔ)言使用類似卡儒。
比如流程控制語(yǔ)句 if-else 田柔、循環(huán)(for,while)骨望。
比如數(shù)據(jù)結(jié)構(gòu)數(shù)組等硬爆。
它們只能用在動(dòng)作里。
如下示例使用if-else統(tǒng)計(jì)第二列大于4462177 和小于4462177的分別有多少行擎鸠。
$ awk 'NR>2{if($2>4462177) more+=1; else less+=1} END{print "more:",more,"less:",less}' population.txt
more: 24 less: 7
上面這個(gè)例子也可以拆分成多個(gè)”模式-動(dòng)作”來實(shí)現(xiàn)缀磕。
$ awk 'NR>2 && $2>4462177{more+=1} NR>2 && $2<=4462177{less+=1} END{print "more:",more,"less:",less}' population.txt
more: 24 less: 7
再來看個(gè)for循環(huán)的例子,打印AWK的命令行參數(shù)劣光。
命令行參數(shù)在輸入文件后追加就可以傳入袜蚕。
$ awk 'BEGIN {for(i=0;i<ARGC;i++) printf "%s\t",ARGV[i]; print ""}' population.txt abc def cdg
awk population.txt abc def cdg
ARGC和ARGV也是AWK的內(nèi)建變量,跟C語(yǔ)言的參數(shù)結(jié)構(gòu)差不多绢涡。
ARGC:命令行參數(shù)的個(gè)數(shù)牲剃。
ARGV:命令行參數(shù)數(shù)組。
// 等價(jià)于C語(yǔ)言
int main(int argc, char *argv[])
AWK也支持使用數(shù)組進(jìn)行數(shù)據(jù)存儲(chǔ)雄可。
如下示例將對(duì)輸入行進(jìn)行倒序輸出凿傅。
$ awk '{addr[NR]=$1} END{i=NR; while(i>0){print i,addr[i];i-=1}}' population.txt|head -n 5
33 新疆
32 寧夏
31 青海
30 甘肅
29 陜西
正則表達(dá)式
AWK 提供了對(duì)正則表達(dá)式的支持缠犀,正則表達(dá)式放在一對(duì)斜杠里:/regexpr/ 。
AWK使用 “~” 符號(hào)表示字符串匹配聪舒,”!~” 符號(hào)表示不匹配夭坪。
所以我們可以在模式中判斷一個(gè)字符串是否匹配一個(gè)正則表達(dá)式。
如下示例對(duì) 第一列含有 “北” 且第二列不包含 “88” 的行 進(jìn)行打印过椎。
$ awk '$1 ~ /北/ {print}' population.txt
北京 10498288 1582574 1871181 7044533
河北 8297279 4263957 2628649 1404673
湖北 9250228 4445565 3791051 1013612
$ awk '$1 ~ /北/ && $2 !~ /88/ {print}' population.txt
河北 8297279 4263957 2628649 1404673
湖北 9250228 4445565 3791051 1013612
如果判斷整行是否匹配室梅,可以省略 “~” 的左值,如下所示疚宇。
### /regexpr/ 等價(jià)于 $0 ~ /regexpr/
### !/regexpr/ 等價(jià)于 $0 !~ /regexpr/
$ awk '!/西/ && /88/ {print}' population.txt
北京 10498288 1582574 1871181 7044533
內(nèi)蒙古 7170889 2732591 2994117 1444181
福建 11074525 3162036 3598887 4313602
湖南 7898815 4170436 3003397 724982
海南 1843430 586432 668535 588463
青海 1140954 351988 470531 318435
正則表達(dá)式的語(yǔ)法細(xì)節(jié)本文不過多說明亡鼠。
以下是幾個(gè)小例子可以參考:
### 匹配小寫字母開頭的字符串
$ awk '/^[a-z]/' <<< "`echo -e "apple333\n1999fds\nhaode3232\n4343...\nhaoya328"`"
apple333
haode3232
haoya328
### 驗(yàn)證是否是11位國(guó)內(nèi)手機(jī)號(hào)碼
$ awk '/^1[3584][0-9]{9}$/' <<< "`echo -e "18894465939\n1364483882\n13644838825\n23443243432\n1334funny"`"
18894465939
13644838825
進(jìn)階用法
接下來?yè)Q個(gè)內(nèi)容豐富的數(shù)據(jù)集來演示。
以下是 豆瓣電影評(píng)分Top250 的 CSV數(shù)據(jù)集敷待。
### 數(shù)據(jù)格式:排行,電影名,評(píng)分,年份,導(dǎo)演,標(biāo)簽,星級(jí)
$ cat douban_top250.csv|head -n 5
rank,title,rating_num,year,director,quote,star
1,肖申克的救贖,9.7,1994,弗蘭克·德拉邦特 Frank Darabont,希望讓人自由间涵。,2304569
2,霸王別姬,9.6,1993,陳凱歌 Kaige Chen,風(fēng)華絕代。,1709820
3,阿甘正傳,9.5,1994,羅伯特·澤米吉斯 Robert Zemeckis,一部美國(guó)近現(xiàn)代史榜揖。,1733112
4,這個(gè)殺手不太冷,9.4,1994,呂克·貝松 Luc Besson,怪蜀黍和小蘿莉不得不說的故事勾哩。,1913405
AWK默認(rèn)按照 空格/Tab 對(duì)每一個(gè)輸入行進(jìn)行切分。
我們可以使用 -F 參數(shù)進(jìn)行指定分隔符举哟,也支持多個(gè)分隔符思劳。
### 指定分隔符
$ awk -F',' '{print $1,$2,$3}' douban_top250.csv|head -n 3
rank title rating_num
1 肖申克的救贖 9.7
2 霸王別姬 9.6
### 多個(gè)分隔符 可以看到評(píng)分被切分了
$ awk -F'[,.]' '{print $1,$2,$3,$4}' douban_top250.csv|head -n 3
rank title rating_num year
1 肖申克的救贖 9 7
2 霸王別姬 9 6
AWK支持使用shell重定向運(yùn)算符 > 和 >> ,可以對(duì)文件進(jìn)行拆分妨猩。
如下將 評(píng)分9以上的另存為douban_more_9.csv潜叛,評(píng)分9以下的為douban_less_9.csv。
$ awk -F',' 'NR>1 && $3>=9 {print $0 > "douban_more_9.csv"} NR >1 && $3<9 {print $0 > "douban_less_9.csv"}' douban_top250.csv
$ cat douban_less_9.csv|head -n 5
61,讓子彈飛,8.9,2010,姜文 Wen Jiang,你給我翻譯翻譯壶硅,神馬叫做TMD的驚喜威兜。,1294845
63,綠皮書,8.9,2018,彼得·法雷里 Peter Farrelly,去除成見,需要勇氣庐椒。,1245160
65,本杰明·巴頓奇事,8.9,2008,大衛(wèi)·芬奇 David Fincher,在時(shí)間之河里感受溺水之苦椒舵。,788815
68,看不見的客人,8.8,2016,奧里奧爾·保羅 Oriol Paulo,你以為你以為的就是你以為的。,965038
69,西西里的美麗傳說,8.9,2000,朱塞佩·托納多雷 Giuseppe Tornatore,美麗無罪约谈。,781719
$ cat douban_more_9.csv|head -n 5
1,肖申克的救贖,9.7,1994,弗蘭克·德拉邦特 Frank Darabont,希望讓人自由笔宿。,2304569
2,霸王別姬,9.6,1993,陳凱歌 Kaige Chen,風(fēng)華絕代。,1709820
3,阿甘正傳,9.5,1994,羅伯特·澤米吉斯 Robert Zemeckis,一部美國(guó)近現(xiàn)代史窗宇。,1733112
4,這個(gè)殺手不太冷,9.4,1994,呂克·貝松 Luc Besson,怪蜀黍和小蘿莉不得不說的故事措伐。,1913405
5,泰坦尼克號(hào),9.4,1997,詹姆斯·卡梅隆 James Cameron,失去的才是永恒的。,1695453
AWK也支持三目表達(dá)式军俊,上面語(yǔ)句等價(jià)于下面侥加。
$ awk -F',' 'NR>1 {print $0 > ($3>=9 ? "douban_more_9.csv":"douban_less_9.csv")}' douban_top250.csv
同時(shí)我們可以對(duì)文件進(jìn)行批量處理。
比如下面提取第二列和最后一列進(jìn)行MySQL入庫(kù)粪躬。
這在數(shù)據(jù)量大的時(shí)候很管用担败。
比如幾萬(wàn)昔穴、幾億的數(shù)據(jù)可以快速轉(zhuǎn)化為SQL語(yǔ)句。
### 注意 雙引號(hào)只需要斜杠轉(zhuǎn)義:\"
### 單引號(hào)除了斜杠轉(zhuǎn)義還要用''包圍起來: '\''
$ awk -F',' 'NR>1 {print "insert into `movie` (name,star) values ('\''"$2"'\'','\''"$NF"'\'');" > "movie.sql"}' douban_top250.csv
cat movie.sql|head -n 5
insert into `movie` (name,star) values ('肖申克的救贖','2304569');
insert into `movie` (name,star) values ('霸王別姬','1709820');
insert into `movie` (name,star) values ('阿甘正傳','1733112');
insert into `movie` (name,star) values ('這個(gè)殺手不太冷','1913405');
insert into `movie` (name,star) values ('泰坦尼克號(hào)','1695453');
統(tǒng)計(jì)Top250里各個(gè)評(píng)分所占數(shù)量提前。
$ awk -F',' 'NR>1{count[$3]++} END{for(i in count) print "豆瓣電影Top250里評(píng)分",i,"的電影有",count[i],"個(gè)"}' douban_top250.csv
豆瓣電影Top250里評(píng)分 9.0 的電影有 20 個(gè)
豆瓣電影Top250里評(píng)分 9.1 的電影有 23 個(gè)
豆瓣電影Top250里評(píng)分 9.2 的電影有 19 個(gè)
豆瓣電影Top250里評(píng)分 9.3 的電影有 17 個(gè)
豆瓣電影Top250里評(píng)分 9.4 的電影有 6 個(gè)
豆瓣電影Top250里評(píng)分 9.5 的電影有 4 個(gè)
豆瓣電影Top250里評(píng)分 9.6 的電影有 2 個(gè)
豆瓣電影Top250里評(píng)分 9.7 的電影有 1 個(gè)
豆瓣電影Top250里評(píng)分 8.3 的電影有 1 個(gè)
豆瓣電影Top250里評(píng)分 8.4 的電影有 3 個(gè)
豆瓣電影Top250里評(píng)分 8.5 的電影有 11 個(gè)
豆瓣電影Top250里評(píng)分 8.6 的電影有 25 個(gè)
豆瓣電影Top250里評(píng)分 8.7 的電影有 42 個(gè)
豆瓣電影Top250里評(píng)分 8.8 的電影有 38 個(gè)
豆瓣電影Top250里評(píng)分 8.9 的電影有 38 個(gè)
找出Top250里拍過多個(gè)電影的導(dǎo)演吗货。
$ awk -F',' 'NR>1{print $5}' douban_top250.csv|sort|uniq -c|sort -rn|head -n 5
8 宮崎駿 Hayao Miyazaki
7 克里斯托弗·諾蘭 Christopher Nolan
6 史蒂文·斯皮爾伯格 Steven Spielberg
5 王家衛(wèi) Kar Wai Wong
5 李安 Ang Lee
找出Top250里即拍過評(píng)分9以上 又拍過9分以下的導(dǎo)演。
即求 douban_less_9.csv 和 douban_more_9.csv 兩個(gè)文件的交集狈网。
$ awk -F',' 'NR==FNR{map[$5]++} NR>FNR{if($5 in map)print $5}' douban_less_9.csv douban_more_9.csv|sort|uniq -c
1 Chris Columbus
2 李安 Ang Lee
1 姜文 Wen Jiang
1 大衛(wèi)·芬奇 David Fincher
1 羅伯·萊納 Rob Reiner
1 劉偉強(qiáng) / 麥兆輝
1 黑澤明 Akira Kurosawa
1 楊德昌 Edward Yang
4 宮崎駿 Hayao Miyazaki
2 劉鎮(zhèn)偉 Jeffrey Lau
1 詹姆斯·卡梅隆 James Cameron
2 朱塞佩·托納多雷 Giuseppe Tornatore
3 史蒂文·斯皮爾伯格 Steven Spielberg
1 是枝裕和 Hirokazu Koreeda
2 弗朗西斯·福特·科波拉 Francis Ford Coppola
3 克里斯托弗·諾蘭 Christopher Nolan
數(shù)組的key可以字符串拼接宙搬,這樣可以間接實(shí)現(xiàn)二維數(shù)組的邏輯。
$ awk -F',' 'NR==2,NR==5{a[$1"-"$2]=$3} END {for (i in a) print i, a[i]}' douban_top250.csv
1-肖申克的救贖 9.7
3-阿甘正傳 9.5
4-這個(gè)殺手不太冷 9.4
2-霸王別姬 9.6
數(shù)據(jù)統(tǒng)計(jì)的大部分需求都可以用AWK快速的實(shí)現(xiàn)拓哺。
比如:過濾勇垛、統(tǒng)計(jì)、聚合士鸥、并集闲孤、交集、差集等烤礁。
快來試試吧K匣!脚仔!
本文所有用到的數(shù)據(jù)集可以在奇跡狗狗后臺(tái)回復(fù):”awk” 進(jìn)行獲取
附錄
常見的內(nèi)建變量
內(nèi)建變量 | 補(bǔ)充默認(rèn)值 含義 |
---|---|
NF | 當(dāng)前記錄的字段個(gè)數(shù)勤众,即總共多少列 |
NR | 讀取到的記錄數(shù),即當(dāng)前行號(hào) |
FNR | 當(dāng)前輸入文件的記錄個(gè)數(shù)玻侥,區(qū)別于NR决摧,NR表示整體的記錄數(shù)亿蒸,F(xiàn)NR表示當(dāng)前文件 |
ARGC | 命令行參數(shù)的個(gè)數(shù) |
ARGV | 命令行參數(shù)數(shù)組 |
FS | 指定輸入行的字段分割符 |
FILENAME | 當(dāng)前輸入文件名 |
OFS | 指定輸出字段分割符 |
ORS | 指定輸出的記錄分割符 默認(rèn)是換行 "\n" |
RS | 指定輸入行的記錄分割符 默認(rèn)是換行 "\n" |
常見的內(nèi)建函數(shù)
函數(shù) | 含義 |
---|---|
length(s) | 字符串s長(zhǎng)度 |
tolower(s) | 把字符串轉(zhuǎn)為小寫 |
substr(s, p) | 字符串s的子串凑兰,從位置p開始到末尾 |
split(s, a, fs) | 把字符串s根據(jù)fs進(jìn)行分割,存到數(shù)組a中 |
sprintf(fmt,expr-list) | 跟C語(yǔ)言sprintf一樣边锁,用于字符串格式化 |
int(x) | 取x 的整數(shù)部分 |
sin(x) / cos(x) / sqrt(x) | 正弦 / 余弦 / 平方根 |
rand() | 隨機(jī)數(shù) 配合 srand(x)使用 x 是 rand() 的隨機(jī)數(shù)種子 |
match(s,r) | 正則表達(dá)式匹配姑食,測(cè)試 s 是否包含能被 r 匹配的子串 |
sub(r,s) | 正則表達(dá)式替換,將 $0 的第一個(gè)被r匹配的子串替換為s |
gsub(r,s) | 正則表達(dá)式全局替換茅坛,將 $0 中所有被r匹配的子串替換為s |