本文概述
在许多情况下, 你需要快速分析, 修改和处理数量和大小较大的文件。但是, 文件通常是基于文本的逗号分隔值(CSV)文件。使用标准电子表格应用程序(例如Excel, LibreOffice或OpenOffice)打开它们会使计算机的内存过载。另外, 由于一些意外的文件异常, 一批处理大量文件通常在几个小时后失败。
你不可避免地会在冻结屏幕, 重新启动和长时间等待上花费大量时间。
但是, 大多数这些任务可以用几行代码来完成。而且, 熟悉一些简单的shell命令行可以大大节省时间并减少挫败感。
这篇文章将概述我几乎每天都使用的一些shell命令。希望你觉得它们有用。
快速笔记
请注意, shell的类型不同(bash, zsh等), 其中bash shell最为常见, 因为它是OS X和主要linux发行版上的默认shell。 Zsh(还有Oh My Zsh)是一种流行且功能强大的替代方案。此博客文章中包含的shell命令已经在OS X(macOS)上的bash上进行了测试, 并且可以与其他shell和环境一起使用。
以下所有示例均基于UCI机器学习存储库中的成人数据集, 也称为”普查收入”数据集, 可在此处获取。此数据集通常用于根据普查数据预测收入是否超过\ $ 50K /年。拥有48842行和14个属性, 到目前为止, 它并不是一个很大的数据集, 但足以说明这些示例。尽管数据以.data扩展名存储, 但它是格式正确的CSV文件。随时下载数据集以继续学习!
用wc计数
给定一个基于文本的新文件, 你想知道它包含多少行。这可以通过单词count wc -l命令来完成:
$ wc -l adult.data
32562 adult.data
告诉你Adult.data文件包含32562行。 -l标志告诉wc计算行数。但是你也可以使用wc代替-w标志来计数单词。
$ wc -w adult.data
488415 adult.data
adult.data文件包含近50万个字。
通过使用简单的ls -l命令的输出作为wc的输入, wc命令还可以计算目录中的文件数。使用管道符号|将命令的输出用作另一个命令的输入是一种有用的shell模式, 称为流水线。在整个文章中, 你将在几个示例中看到它。
假设现在你有一个包含许多文件的文件夹。以下命令将准确告诉你它包含多少个文件:
$ ls -l <folder> | wc -l
508
如果你开始添加通配符和子文件夹, 则wc还有许多其他应用程序。
让我们回到你的adult.data文件, 并使用head命令查看第一行。默认情况下, head输出10行, 你可以使用-n标志将输出限制为前2行。
$ head -n 2 adult.data
39, State-gov, 77516, Bachelors, 13, Never-married, Adm-clerical, Not-in-family, White, Male, 2174, 0, 40, United-States, <=50K
50, Self-emp-not-inc, 83311, Bachelors, 13, Married-civ-spouse, Exec-managerial, Husband, White, Male, 0, 0, 13, United-States, <=50K
并且你注意到该文件没有标题行。列名不在文件内。你可以通过使用cat命令连接两个文件来添加该标题行。
用cat连接文件
cat命令将文件的内容打印到标准输出(也就是你的终端)上。它也可以用来连接一系列文件。命令cat file_1.csv file_2.csv> target_file.csv会将file_1.csv和file_2.csv的内容合并到target_file.csv中, 并在file_1.csv的末尾添加file_2.csv。
头文件不在原始数据集中, 你需要创建它。为此, 你将以逗号分隔的列名列表回显到header.csv文件中。
$ echo "age, workclass, fnlwgt, education, education-num, marital-status, occupation, relationship, race, sex, capital-gain, capital-loss, native-country, class" > header.csv
在此示例中, 你使用了shell重定向>字符将cat的输出转储到adult.csv文件中。 >将创建文件或完全替换其内容(如果文件已存在)。加倍符号>>会将新内容附加到现有文件中, 而不会擦除其内容。
现在让我们将header.csv文件添加到adult.data文件的开头。同时, 将adult.data重命名为adult.csv, 因为它毕竟是CSV格式的文件。
$ cat header.csv adult.data > adult.csv
检查Adult.csv的第一行是否包含具有以下内容的列名:
$ head -n 1 adult.csv
age, workclass, fnlwgt, education, education-num, marital-status, occupation, relationship, race, sex, capital-gain, capital-loss, native-country, class
39, State-gov, 77516, Bachelors, 13, Never-married, Adm-clerical, Not-in-family, White, Male, 2174, 0, 40, United-States, <=50K
用sed修改文件
当文件损坏或格式错误(例如, 非UTF-8字符或逗号不正确)时, 会发生另一种频繁的数据挖掘情况。你可以纠正该文件, 而无需使用sed命令实际打开它。
通用sed模式是
$ sed "s/<string to replace>/<string to replace it with>/g" <source_file> > <target_file>.
adult.csv数据集使用?表示缺失值的字符。你可以使用sed将其替换为更适合的默认值。最好使用空字符串来表示缺少的值, 因为在将数据加载到Pandas DataFrame中时, 它将被解释为NaN值。
$ grep ", ?, " adult.csv | wc -l
2399
这样一来, 你可以算出2399行, 其中至少一列的缺失值用?表示。以下命令将所有列替换为?用一个空字符串。所有仅包含?, 后跟一个空格的单元格现在将真正为空。
$ sed "s/, ?, /, , /g" adult.csv > adult.csv
请注意, 在源字符串和目标字符串中使用列定界符, 以避免替换可能出现在数据集中其他地方的合法问号。
子集大文件
现在想象一下, 你要处理的文件超过3000万行。在制作Python或R脚本时, 你将只想在原始大文件的样本上测试工作流, 而不必将整个数据集加载到内存中。你可以组合使用头和尾来创建原始数据集的子样本。
- head输出文件的第一部分;
- 尾巴输出文件的最后一部分
如你之前所见, 这些命令带有-n标志, 代表–lines, 它限制了输出中的行数。尾部还带有-f标志, 其中f代表–follow, 当将行添加到文件末尾时, 它将输出附加数据。当脚本使用tail -f <logfile>运行时, 这对于监视日志文件特别方便。
混合在管道符号中|使用头和尾, 你可以从源文件中提取一定数量的行, 并将内容导出到子文件。例如, 要提取从第100行开始的20行:
$ head -n 120 adult.csv | tail -n 20 > adult_sample.csv
请注意, 你首先将first_line + number_of_lines放在头, 然后在number_of_lines尾部放置。通用子设置命令为:
$ head -n <total_lines> <source_file> | tail -n <number_of_lines> > <target_file>
其中total_lines = first_line + number_of_lines。但是, 新的采样文件不再包含标题行。我们已经知道如何使用cat将标头重新添加到子文件中, 以连接子文件和之前创建的header.csv文件:
$ cat adult_sample.csv header.csv > adult_sample_with_header.csv
请注意, 你没有使用源文件名” adult \ _sample.csv”作为目标文件名, 而是创建了一个名为” adult \ _sample \ _with \ _header.csv”的新文件。使用cat时与源之一和目标名称相同, 将导致意外的文件内容。最好创建一个新文件作为cat命令的输出。
使用uniq查找重复项
使用uniq命令, 你可以在文件中找到相邻的重复行。 uniq带有几个标志, 更有用的是:
- uniq -c:将重复计数添加到每一行;
- uniq -d:仅输出重复的行;和
- uniq -u:仅输出唯一的行。
但是, uniq不是明智的命令。如果重复的行不相邻, 则不会被检测到。这意味着你首先需要对文件进行排序。此命令将统计adult.csv中重复的行数。
$ sort adult.csv | uniq -d | wc -l
23
并显示有23个重复项。下一条命令获取具有重复计数的所有行的输出, 以反向排序并输出前3个重复项:
$ sort adult.csv | uniq -c | sort -r | head -n 3
3 25, Private, 195994, 1st-4th, 2, Never-married, ...
2 90, Private, 52386, Some-college, 10, Never-married, ...
2 49, Self-emp-not-inc, 43479, Some-college, 10, Married-civ-spouse, ...
通过将sort和uniq与不同的标志结合使用, 可以获得许多强大的选项。使用man sort和man uniq进一步探索这些命令。
选择带有剪切的列
CSV文件和Shell命令的妙处在于, 你还可以通过使用cut选择特定列来在列级别进行工作。 cut具有两个主要标志:-d指定列定界符, 和-f指定要处理的列。在下面的示例中, 你将使用cut来查找分类变量workclass(第2列)采用的唯一值的数量。
首先选择列工作类并直达标题以确认你具有正确的列:
$ cut -d ", " -f 2 adult.csv | head -3
workclass
State-gov
Self-emp-not-inc
现在, 要计算唯一性, 你可以对cut的输出进行排序并将结果通过管道传递给uniq -c, 如下所示:
$ cut -d ", " -f 2 adult.csv | sort | uniq -c
1837
960 Federal-gov
2093 Local-gov
7 Never-worked
22696 Private
1116 Self-emp-inc
2541 Self-emp-not-inc
1298 State-gov
14 Without-pay
1 workclass
例如, 这告诉你你有1837个空值, 并且主类到目前为止是具有22969次出现的Private类。
上面的命令行类似于应用于包含Adult.csv数据的DataFrame的value_counts()方法。
循环播放
到目前为止, 你基本上只处理一个文件。要重命名, 处理或传输大量文件, 应将循环添加到我们的Shell工具箱中。 bash shell中循环的通用格式为:
while true; do
_do something_ ;
done
让我们开始吧。假设你有1000个文件名, 文件名中带有空格, 而你想用一个非下划线的” _”替换每个空格。
replace_source=' '
replace_target='_'
for filename in ./*.csv; do
new_filename=${filename//$replace_source/$replace_target}
mv "$filename" "$new_filename"
done
在这个例子中
- 首先, 声明两个变量:replace_source和replace_target作为空格和下划线字符的占位符
- 你遍历当前文件夹中的所有* .csv文件
- 对于每个文件名, 你可以通过用下划线替换每个空格来创建一个new_filename
- 然后使用mv命令将文件从当前文件名重命名为新文件名
实际上, 不仅使用shell循环浏览目录中的文件, 还创建了变量。
变量
你将以变量结尾此shell命令介绍。在shell中创建变量只需通过
$ varname='<a string>'
$ varname=a number
例如:
$ varname='Hello world'
$ varname=123.4
请注意, ” =”符号周围没有空格。 var ='<a string>’将不起作用。要返回变量值, 只需回显它:
$ echo $varname
123.4
要在脚本中使用它, 请将其封装在引号中, 如前所述:
$ mv "$filename" "$new_filename"
编写循环和使用变量为更复杂的文件操作打开了方便之门, 并且是shell脚本的门户, 这超出了本文的介绍。但是, 如果你从小处着手, 并随着信心的提高而提高, 那么shell脚本就足够简单了。
总结
Shell命令行作为数据科学家在你的日常工作中非常有用。还有更多示例和用例可以探索。正如你将看到的, 使用各种各样的可用shell命令和相关标志通常总是有不同的方法来达到特定的结果。在这种情况下, 保持简单永远是制胜法宝。
希望这里提供的示例将帮助你将Shell命令集成到数据处理工具包中。随时与我联系并在Twitter上分享你的Shell技巧:@alexip。
评论前必须登录!
注册