数据分析师眼中的awk命令
事实上,awk 并不是一个简单的命令,它也是一种编程语言,编程语言允许我们通过特定的指令来控制计算机的工作,这也是 awk 强大的关键原因。如果要使用模糊匹配,可以使用正则表达式,例如将上面的需求修改为:统计每个城市岗位名称(job_name 列) 包含“Python”或“数据分析”且薪资在20(K)以上的岗位的平均薪资,利用 awk 命令同样可以轻松实现。是 awk 命令的内置变量,表示已读取的
对于日常使用 Linux 操作系统的人来说,awk 这个命令是再熟悉不过了,它和 sed、grep 命令并称为 Linux 系统文本处理三剑客。awk 命令来自于三位创始人 Alfred Aho、Peter Weinberger 和 Brian Kernighan 的姓氏缩写,其中 Brian Kernighan 是普林斯顿大学的教授,他就是那个建议 Ken Thompson 将自己的操作系统命名为 Unix 的 Kernighan,也是和 Dennis Ritchie 合著了旷世奇书 The C Programming Language 的 Kernighan,大名鼎鼎的 cron(基于时间的任务管理系统)也是 Kernighan 的作品。
事实上,awk 并不是一个简单的命令,它也是一种编程语言,编程语言允许我们通过特定的指令来控制计算机的工作,这也是 awk 强大的关键原因。接下来,我们以数据分析师的视角来看看如何使用 awk 命令实现数据处理。此处,我们待处理的数据放在一个 CSV 文件中,该文件包含了一些招聘岗位的信息,我们先通过下面一组命令来了解它。
查看文件的性质。
file jobs.csv
输出:
jobs.csv: UTF-8 Unicode text
查看文件的行数。
wc -l jobs.csv
输出:
9826 jobs.csv
查看文件最前面的10行。
head -10 jobs.csv
输出:
id,company_name,salary,year,edu,job_name,job_type,city,district,stree
1,软通动力集团,12.5,1-3年,本科,Python开发,Python,成都,武侯区,草金立交
2,思湃德,30.0,3-5年,本科,Python,Python,成都,双流区,华阳
3,源码时代,17.5,3-5年,大专,Python 讲师,Python,成都,武侯区,石羊
4,三源合众,8.0,1年以内,本科,Python,Python,成都,武侯区,新会展
5,软通动力,10.5,1-3年,本科,Python开发,Python,成都,武侯区,机投
6,中佳业,7.5,3-5年,大专,C++/Python,Python,成都,武侯区,肖家河
7,知行天下,9.5,1年以内,大专,Python讲师,Python,成都,龙泉驿区,龙泉
8,川大智胜,10.5,3-5年,本科,Python,Python,成都,武侯区,保利花园
9,电科荷福研究院,9.5,3-5年,本科,Python后端开发,Python,成都,郫都区,高新西
查看文件最后面的20行。
tail -20 jobs.csv
输出:
9806,平安普惠,6.5,应届生,大专,金融产品经理,产品经理,西安,碑林区,南大街
9807,金都商务,7.5,经验不限,高中,信用卡产品经理,产品经理,西安,碑林区,建设路
9808,百善财富,30.0,1年以内,本科,互金产品经理,产品经理,西安,碑林区,长安路
9809,勺子科技,8.0,3-5年,本科,产品经理,产品经理,西安,,
9810,天合云创,10.5,3-5年,本科,网络安全产品经理,产品经理,西安,雁塔区,高新软件园
9811,陕西仁明医药,3.5,3-5年,本科,医药产品经理,产品经理,西安,莲湖区,高新路
9812,伯图大数据西安分公司,17.5,3-5年,本科,高级产品经理,产品经理,西安,,
9813,伯图大数据西安分公司,8.0,1-3年,本科,产品经理,产品经理,西安,,
9814,荣峰科技,10.5,3-5年,本科,产品经理,产品经理,西安,,
9815,西安图迹信息科技,10.5,3-5年,本科,产品经理,产品经理,西安,,
9816,时代顾邦教育,10.0,3-5年,本科,产品经理(薪资:8-12K),产品经理,西安,雁塔区,唐延路
9817,驴妈妈,6.0,5-10年,本科,出境旅游产品经理,产品经理,西安,灞桥区,浐灞半岛
9818,多米诺科技,8.5,1-3年,本科,产品经理,产品经理,西安,雁塔区,长延堡
9819,积木家互联网装修,9.0,3-5年,本科,产品经理,产品经理,西安,未央区,未央路
9820,美欣,11.0,10年以上,本科,数据产品经理,产品经理,西安,,
9821,公众智能,9.0,3-5年,本科,产品经理,产品经理,西安,,
9822,微感,9.0,3-5年,大专,产品经理,产品经理,西安,雁塔区,紫薇田园都市
9823,巴斯光年,15.0,3-5年,本科,产品经理,产品经理,西安,雁塔区,大雁塔
9824,西大华特科技,6.5,1-3年,硕士,产品经理(农药),产品经理,西安,雁塔区,唐延路
9825,西安纯粹科技,4.5,1-3年,本科,产品经理,产品经理,西安,雁塔区,玫瑰大楼
接下来我们就可以体验 awk 命令的强大了,首先我们从上面的文件中取出公司名称(company_name 列)、职位名称(job_name 列)和薪资(salary 列),这个操作类似于 SQL 中的投影。
awk -F ',' '{print $2, $6, $3}' jobs.csv
其中,-F
参数用来指定的列的分隔符,$2
表示第二列,以此类推。写在{}
中的代码会作用到jobs.csv
的每一行。我们假设上面的数据保存在数据库中名为jobs
的表中,实现相同功能的 SQL 如下所示。
SELECT company_name, job_name, salary FROM jobs;
如果使用 MySQL 或 Hive,我们可以通过limit
子句来限制查询结果的行数,如下所示。
SELECT company_name, job_name, salary FROM jobs LIMIT 10;
使用 awk 命令的做法如下所示,其中的NR
是 awk 命令的内置变量,表示已读取的记录数,在读取完第11行记录后,NR <= 11
条件不再成立。
awk -F ',' 'NR <= 11{print $2, $6, $3}' jobs.csv
输出:
company_name job_name salary
软通动力集团 Python开发 12.5
思湃德 Python 30.0
源码时代 Python 讲师 17.5
三源合众 Python 8.0
软通动力 Python开发 10.5
中佳业 C++/Python 7.5
知行天下 Python讲师 9.5
川大智胜 Python 10.5
电科荷福研究院 Python后端开发 9.5
傲梦网络科技 Python线上试听课老师 5.5
如果我们要取出薪资大于等于60(K)的公司名称、岗位名称、城市和薪资,可以使用下面的命令。
awk -F ',' '{if ($3 >= 60) print $2, $6, $8, $3}' jobs.csv
输出:
company_name job_name city salary
vivo 高级数据分析经理(003524) 南京 65.0
鳄鱼涂图 互联网运营总监 南京 65.0
vivo Java研发专家 深圳 65.0
慧择信息集团 Java架构师 深圳 60.0
慧择信息集团 首席Java 架构师 深圳 95.0
货拉拉科技 资深数据分析师(J11772) 深圳 60.0
vivo 流量产品经理 深圳 65.0
vivo 数据产品经理 深圳 65.0
好未来 Java架构师 北京 65.0
宝誉德 Java架构师 北京 65.0
蚂蚁金服 Java 杭州 60.0
多准数据 数据分析总监 杭州 65.0
博世猫网络科技 数据分析总监 杭州 75.0
对应的 SQL 如下所示。
SELECT company_name, job_name, city, salary FROM jobs WHERE salary >= 60;
如果我们希望统计每个城市的招聘岗位数量,使用 awk 也可以做到。
awk -F ',' 'NR > 1 {a[$8] += 1} END{for (i in a) print i, a[i]}' jobs.csv
其中,NR > 1
是为了去掉表头所在的行。
输出:
武汉 1017
上海 1181
南京 1075
广州 1124
杭州 1175
北京 1167
深圳 1187
西安 905
成都 994
在 awk 中,BEGIN{}
用来放置用来放置开始处理文件前要执行的语句,而END{}
用来放置处理完所有的行之后要执行的语句,这样我们可以先根据第8列(city 列)进行计数,并将计数的结果保存名为a
的数组变量中,最后再通过for
循环从数组中取出每个城市的名称和对应的人数。上面的操作跟 SQL 中的group by
效果一致。
SELECT city, COUNT(*) AS total FROM jobs GROUP BY city;
如果需要按照招聘岗位的数量从高到低对城市排序,在 SQL 中可以使用order by
子句,如下所示。
SELECT city, COUNT(*) AS total FROM jobs GROUP BY city ORDER BY total DESC;
对于 Linux 系统,我们可以通过管道操作,用 sort 命令对 awk 命令的执行结果进行排序,如下所示。
awk -F ',' 'NR > 1 {a[$8] += 1} END{for (i in a) print i, a[i]}' jobs.csv | sort -nrk 2
其中,sort 命令的参数-n
表示根据数值排序,-r
表示逆序(从大到小),-k
表示根据第2列进行排序。
输出:
深圳 1187
上海 1181
杭州 1175
北京 1167
广州 1124
南京 1075
武汉 1017
成都 994
西安 905
根据上面的讲解,相信大家已经知道如何统计每个城市岗位类型(job_type 列)为“Python”的岗位的平均薪资了,快动手试一试,然后再看看跟我给的参考答案是否一致。
awk -F ',' '{if ($7 == "Python") {a[$8] += 1; b[$8] += $3}} END{for (i in a) printf "%s %.1f\n", i, b[i]/a[i]}' jobs.csv
上面的命令中,由于if
条件成立时需要执行计数和累加薪资两项操作,所以我们将其置于一对花括号中,表示两个语句都是if
条件成立时要执行的语句。此外,我们用了 printf 对输出进行格式化处理,将平均薪资四舍五入保留1位小数,这个语法对于熟悉 C 或 Python 等编程语言的小伙伴来说并不陌生。
输出:
武汉 12.9
上海 18.1
南京 14.2
广州 14.9
杭州 17.7
北京 22.9
深圳 18.6
西安 11.7
成都 15.0
如果要使用模糊匹配,可以使用正则表达式,例如将上面的需求修改为:统计每个城市岗位名称(job_name 列) 包含“Python”或“数据分析”且薪资在20(K)以上的岗位的平均薪资,利用 awk 命令同样可以轻松实现。
awk -F ',' '{if ($6 ~ /Python|数据分析/ && $3 >= 20) {a[$8] += 1; b[$8] += $3}} END{for (i in a) printf "%s %.1f\n", i, b[i]/a[i]}' jobs.csv
其中,~
运算符表示基于正则表达式的模糊匹配,反向匹配的运算符是!~
。
输出:
武汉 22.4
上海 26.5
南京 28.2
广州 25.6
杭州 27.7
北京 28.1
深圳 27.4
西安 24.2
成都 24.1
最后,我们简单总结一下 awk 中的运算符、内置变量和内置函数给大家做一个参考。
表1:awk 运算符。
运算符 | 描述 |
---|---|
=、+=、-=、*=、/=、%=、**=、^= | 赋值运算符 |
? : | 条件运算符 |
||、&&、! | 逻辑运算符 |
~、!~ | 正向/反向匹配(正则表达式)运算符 |
<、<=、>、>=、!=、== | 关系运算符 |
空格 | 连接运算符 |
+、-、*、/、%、^、** | 算术运算符 |
++、-- | 自加/自减运算符 |
$ | 引用运算符 |
in | (数组)成员运算符 |
表2:awk (部分)内置变量。
变量 | 描述 |
---|---|
$n | 当前记录的第 n 个字段,字段间由 FS 分隔 |
$0 | 完整的输入记录 |
ARGC | 命令行参数的数目 |
ARGV | 包含命令行参数的数组 |
ERRNO | 最后一个系统错误的描述 |
FILENAME | 当前文件名 |
FNR | 各文件分别计数的行号 |
FS | 字段分隔符(默认是任何空格) |
IGNORECASE | 如果为真,则进行忽略大小写的匹配 |
NF | 一条记录的字段的数目 |
NR | 已经读出的记录数,就是行号,从1开始 |
OFMT | 数字的输出格式(默认值是%.6g) |
OFS | 输出字段分隔符,默认值与输入字段分隔符一致。 |
ORS | 输出记录分隔符(默认值是一个换行符) |
RS | 记录分隔符(默认是一个换行符) |
需要说明的是,我们可以通过 awk 命令的参数来修改内置变量的值,例如-F
参数修改了内置变量FS
的值。我们可以在 awk 的代码中来修改内置变量的值,如下所示。
awk 'BEGIN{FS=","; OFS="::"} NR <= 11{print $2, $6, $3}' jobs.csv
输出:
company_name::job_name::salary
软通动力集团::Python开发::12.5
思湃德::Python::30.0
源码时代::Python 讲师::17.5
三源合众::Python::8.0
软通动力::Python开发::10.5
中佳业::C++/Python::7.5
知行天下::Python讲师::9.5
川大智胜::Python::10.5
电科荷福研究院::Python后端开发::9.5
傲梦网络科技::Python线上试听课老师::5.5
awk 内置的函数也非常丰富,包括数学函数、字符串函数、时间函数等,此处我们不做介绍,有兴趣的读者可以在网上找到很多介绍 awk 函数的资源,下面仅简单举几个例子。
替换字符串。
awk 'BEGIN{string = "banana"; gsub("a", "@", string); print string}'
输出:
b@n@n@
截取字符串。
awk 'BEGIN{string = "banana"; print substr(string, 2, 4)}'
输出:
anan
格式化时间日期。
awk 'BEGIN{print strftime("%Y-%m-%d", systime())}'
输出:
2022-07-31
更多推荐
所有评论(0)