【发布时间】:2011-06-22 18:06:31
【问题描述】:
我有包含几列的制表符分隔文件。我想计算文件夹中所有文件的列中不同值的出现频率,并按计数的递减顺序对它们进行排序(最高计数优先)。我将如何在 Linux 命令行环境中完成此操作?
它可以使用任何常用的命令行语言,如 awk、perl、python 等。
【问题讨论】:
标签: bash command-line frequency
我有包含几列的制表符分隔文件。我想计算文件夹中所有文件的列中不同值的出现频率,并按计数的递减顺序对它们进行排序(最高计数优先)。我将如何在 Linux 命令行环境中完成此操作?
它可以使用任何常用的命令行语言,如 awk、perl、python 等。
【问题讨论】:
标签: bash command-line frequency
此代码计算所有列的出现次数,并为每个列打印一个排序报告:
# columnvalues.pl
while (<>) {
@Fields = split /\s+/;
for $i ( 0 .. $#Fields ) {
$result[$i]{$Fields[$i]}++
};
}
for $j ( 0 .. $#result ) {
print "column $j:\n";
@values = keys %{$result[$j]};
@sorted = sort { $result[$j]{$b} <=> $result[$j]{$a} || $a cmp $b } @values;
for $k ( @sorted ) {
print " $k $result[$j]{$k}\n"
}
}
将文本另存为 columnvalues.pl
运行为:perl columnvalues.pl files*
在顶层while循环中:
* 循环遍历组合输入文件的每一行
* 将行拆分为@Fields 数组
* 对于每一列,递增结果数组哈希数据结构
在顶层 for 循环中:
* 循环遍历结果数组
* 打印列号
* 获取该列中使用的值
* 按出现次数对值进行排序
* 基于值的二级排序(例如 b vs g vs m vs z)
* 使用排序列表遍历结果哈希
* 打印每次出现的值和次数
column 0:
a 3
z 3
t 1
v 1
w 1
column 1:
d 3
r 2
b 1
g 1
m 1
z 1
column 2:
c 4
a 3
e 2
如果您的输入文件是 .csv,请将 /\s+/ 更改为 /,/
在一场丑陋的比赛中,Perl 装备精良。
这个单线做同样的事情:
perl -lane 'for $i (0..$#F){$g[$i]{$F[$i]}++};END{for $j (0..$#g){print "$j:";for $k (sort{$g[$j]{$b}<=>$g[$j]{$a}||$a cmp $b} keys %{$g[$j]}){print " $k $g[$j]{$k}"}}}' files*
【讨论】:
这是在 shell 中执行此操作的一种方法:
FIELD=2
cut -f $FIELD * | sort| uniq -c |sort -nr
这是 bash 擅长的事情。
【讨论】:
-d, 以逗号或任何其他分隔符分隔字段)。
cut -f 1 -d ' '。万分感谢。 :)
查看第二列的频率计数(例如):
awk -F '\t' '{print $2}' * | sort | uniq -c | sort -nr
文件A.txt
z z a
a b c
w d e
文件B.txt
t r e
z d a
a g c
文件C.txt
z r a
v d c
a m c
结果:
3 d
2 r
1 z
1 m
1 g
1 b
【讨论】:
Ruby(1.9+)
#!/usr/bin/env ruby
Dir["*"].each do |file|
h=Hash.new(0)
open(file).each do |row|
row.chomp.split("\t").each do |w|
h[ w ] += 1
end
end
h.sort{|a,b| b[1]<=>a[1] }.each{|x,y| print "#{x}:#{y}\n" }
end
【讨论】:
each_with_object 等。简而言之,这写得有些粗俗。
GNU site 建议使用这个不错的 awk 脚本,它可以打印单词及其频率。
可能的变化:
sort -nr(以及反向word 和freq[word])以降序查看结果。freq[3]++ - 用列号替换 3。这里是:
# wordfreq.awk --- print list of word frequencies
{
$0 = tolower($0) # remove case distinctions
# remove punctuation
gsub(/[^[:alnum:]_[:blank:]]/, "", $0)
for (i = 1; i <= NF; i++)
freq[$i]++
}
END {
for (word in freq)
printf "%s\t%d\n", word, freq[word]
}
【讨论】: