如何按字母顺序然后按列数字对 csv 数据进行排序?
How can I sort csv data alphabetically then numerically by column?
如果我有一组具有重复名称值但每个重复值有不同变化的数据,我如何按每个重复值的顶部排序?希望这是有道理的,但我希望在下面进一步证明我的意思。
以制表符分隔的csv文件中的这组数据为例
Ranking ID Year Make Model Total
1 128 2010 Infiniti G37 128
2 124 2015 Jeep Wrangler 124
3 15 014 Audi S4 120
4 113 2012 Acura Tsx sportwagon 116
5 83 2014 Honda Accord 112
6 112 2008 Acura TL 110
7 65 2009 Honda Fit 106
8 91 2010 Mitsu Lancer 102
9 50 2015 Acura TLX 102
10 31 2007 Honda Fit 102
11 216 2007 Chrystler 300 96
12 126 2010 Volkswagen Eos 92
13 13 2016 Honda Civic 1.5t 92
如果查看“制造商”列,您会看到 Acura 和 Honda 等名称重复出现,但在“型号”和“总计”列中有所不同。假设 csv 文件中有 200 行左右。如何对文件进行排序,以便项目按品牌分组,每个品牌显示的“总计”列下只有三个价值最高的项目?
预期输出低于
Ranking ID Year Make Model Total
1 113 2012 Acura Tsx sportwagon 116
2 112 2008 Acura TL 110
3 50 2015 Acura TLX 106
4 83 2014 Honda Accord 112
5 31 2007 Honda Fit 102
6 13 2016 Honda Civic 1.5t 92
...
到目前为止,这是我的 awk 代码,我什至无法通过这部分来尝试按总列对品牌进行分组
BEGIN {
FS = OFS = "\t";
}
FNR == 1 {
print;
next;
}
FNR > 1 {
a[NR] = ;
}
END {
PROCINFO["sorted_in"] = "@val_str_desc"
for(i = 1; i < FN-1; i++) {
print a[i];
}
}
目前,我的代码读取文本文件,打印 headers(列标题)然后停在那里,它不会继续按字母顺序打印其余数据。有什么想法吗?
以下假定 bash(如果您不使用 bash 将 $'\t'
替换为带引号的实际制表符)和 GNU coreutils。它还假定您要先按 Make
列的字母顺序排序,然后按 Total
的数字降序排序,最后最多保留每个 Make
条目的前 3 个。
排序是sort
的工作,head
和tail
可以用来隔离header行,awk
可以用来每个 Make
最多保留 3 个,并且 re-number 第一列:
$ head -n1 data.tsv; tail -n+2 data.tsv | sort -t$'\t' -k4,4 -k6,6rn |
awk -F'\t' -vOFS='\t' '==p {n+=1} !=p {n=1;p=} {=++r} n<=3'
Ranking ID Year Make Model Total
1 113 2012 Acura Tsx sportwagon 116
2 112 2008 Acura TL 110
3 50 2015 Acura TLX 102
4 15 014 Audi S4 120
5 216 2007 Chrystler 300 96
6 83 2014 Honda Accord 112
7 65 2009 Honda Fit 106
8 31 2007 Honda Fit 102
10 128 2010 Infiniti G37 128
11 124 2015 Jeep Wrangler 124
12 91 2010 Mitsu Lancer 102
13 126 2010 Volkswagen Eos 92
请注意,这与您的预期输出不同:Make
按字母顺序排序(Audi
在 Acura
之后,而不是 Honda
)并且只有 3保留最大的 Total
(112, 106, 102
用于 Honda
,而不是 112, 102, 92
)。
如果您使用 GNU awk
,并且您的输入文件足够小以适合内存,您也可以只用 awk
完成所有这些,这要归功于它的多维数组和它的 asorti
函数,根据索引对数组进行排序:
$ awk -F'\t' -vOFS='\t' 'NR==1 {print; next} {l[][][[=11=]]}
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
for(m in l) {
n = asorti(l[m], t, "@ind_num_desc"); n = (n>3) ? 3 : n
for(i=1; i<=n; i++) for(s in l[m][t[i]]) {[=11=] = s; = ++r; print}
}
}' data.tsv
Ranking ID Year Make Model Total
1 113 2012 Acura Tsx sportwagon 116
2 112 2008 Acura TL 110
3 50 2015 Acura TLX 102
4 15 014 Audi S4 120
5 216 2007 Chrystler 300 96
6 83 2014 Honda Accord 112
7 65 2009 Honda Fit 106
8 31 2007 Honda Fit 102
9 128 2010 Infiniti G37 128
10 124 2015 Jeep Wrangler 124
11 91 2010 Mitsu Lancer 102
12 126 2010 Volkswagen Eos 92
对数组的数组使用 GNU awk 和 sorted_in
:
$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR == 1 {
print
next
}
{
rows[][][++numRows[,]] = [=10=]
}
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
for ( make in rows ) {
PROCINFO["sorted_in"] = "@ind_num_desc"
cnt = 0
for ( total in rows[make] ) {
for ( rowNr=1; rowNr<=numRows[make,total]; rowNr++ ) {
if ( ++cnt <= 3 ) {
row = rows[make][total][rowNr]
print row, cnt
}
}
}
}
}
$ awk -f tst.awk file
Ranking ID Year Make Model Total
4 113 2012 Acura Tsx sportwagon 116 1
6 112 2008 Acura TL 110 2
9 50 2015 Acura TLX 102 3
3 15 014 Audi S4 120 1
11 216 2007 Chrystler 300 96 1
5 83 2014 Honda Accord 112 1
7 65 2009 Honda Fit 106 2
10 31 2007 Honda Fit 102 3
1 128 2010 Infiniti G37 128 1
2 124 2015 Jeep Wrangler 124 1
8 91 2010 Mitsu Lancer 102 1
12 126 2010 Volkswagen Eos 92 1
上面的代码将处理 1 个品牌的多辆汽车总数相同的情况,方法是始终只打印该品牌的前 3 行,例如鉴于此输入,其中 4 个 Acuras 总共有 116 个:
$ cat file
Ranking ID Year Make Model Total
1 128 2010 Infiniti G37 128
2 124 2015 Jeep Wrangler 124
3 15 014 Audi S4 120
4 113 2012 Acura Tsx sportwagon 116
4 113 2012 Acura Foo 116
4 113 2012 Acura Bar 116
4 113 2012 Acura Other 116
5 83 2014 Honda Accord 112
6 112 2008 Acura TL 110
7 65 2009 Honda Fit 106
8 91 2010 Mitsu Lancer 102
9 50 2015 Acura TLX 102
10 31 2007 Honda Fit 102
11 216 2007 Chrystler 300 96
12 126 2010 Volkswagen Eos 92
13 13 2016 Honda Civic 1.5t 92
这是仅显示 4 116 辆 Acuras 中的 3 辆的输出:
$ awk -f tst.awk file
Ranking ID Year Make Model Total
4 113 2012 Acura Tsx sportwagon 116 1
4 113 2012 Acura Foo 116 2
4 113 2012 Acura Bar 116 3
3 15 014 Audi S4 120 1
11 216 2007 Chrystler 300 96 1
5 83 2014 Honda Accord 112 1
7 65 2009 Honda Fit 106 2
10 31 2007 Honda Fit 102 3
1 128 2010 Infiniti G37 128 1
2 124 2015 Jeep Wrangler 124 1
8 91 2010 Mitsu Lancer 102 1
12 126 2010 Volkswagen Eos 92 1
如果这不是您想要的,则将 if ( ++cnt <= 3 )
测试移至外循环或按您希望的方式处理它。
如果我有一组具有重复名称值但每个重复值有不同变化的数据,我如何按每个重复值的顶部排序?希望这是有道理的,但我希望在下面进一步证明我的意思。
以制表符分隔的csv文件中的这组数据为例
Ranking ID Year Make Model Total
1 128 2010 Infiniti G37 128
2 124 2015 Jeep Wrangler 124
3 15 014 Audi S4 120
4 113 2012 Acura Tsx sportwagon 116
5 83 2014 Honda Accord 112
6 112 2008 Acura TL 110
7 65 2009 Honda Fit 106
8 91 2010 Mitsu Lancer 102
9 50 2015 Acura TLX 102
10 31 2007 Honda Fit 102
11 216 2007 Chrystler 300 96
12 126 2010 Volkswagen Eos 92
13 13 2016 Honda Civic 1.5t 92
如果查看“制造商”列,您会看到 Acura 和 Honda 等名称重复出现,但在“型号”和“总计”列中有所不同。假设 csv 文件中有 200 行左右。如何对文件进行排序,以便项目按品牌分组,每个品牌显示的“总计”列下只有三个价值最高的项目?
预期输出低于
Ranking ID Year Make Model Total
1 113 2012 Acura Tsx sportwagon 116
2 112 2008 Acura TL 110
3 50 2015 Acura TLX 106
4 83 2014 Honda Accord 112
5 31 2007 Honda Fit 102
6 13 2016 Honda Civic 1.5t 92
...
到目前为止,这是我的 awk 代码,我什至无法通过这部分来尝试按总列对品牌进行分组
BEGIN {
FS = OFS = "\t";
}
FNR == 1 {
print;
next;
}
FNR > 1 {
a[NR] = ;
}
END {
PROCINFO["sorted_in"] = "@val_str_desc"
for(i = 1; i < FN-1; i++) {
print a[i];
}
}
目前,我的代码读取文本文件,打印 headers(列标题)然后停在那里,它不会继续按字母顺序打印其余数据。有什么想法吗?
以下假定 bash(如果您不使用 bash 将 $'\t'
替换为带引号的实际制表符)和 GNU coreutils。它还假定您要先按 Make
列的字母顺序排序,然后按 Total
的数字降序排序,最后最多保留每个 Make
条目的前 3 个。
排序是sort
的工作,head
和tail
可以用来隔离header行,awk
可以用来每个 Make
最多保留 3 个,并且 re-number 第一列:
$ head -n1 data.tsv; tail -n+2 data.tsv | sort -t$'\t' -k4,4 -k6,6rn |
awk -F'\t' -vOFS='\t' '==p {n+=1} !=p {n=1;p=} {=++r} n<=3'
Ranking ID Year Make Model Total
1 113 2012 Acura Tsx sportwagon 116
2 112 2008 Acura TL 110
3 50 2015 Acura TLX 102
4 15 014 Audi S4 120
5 216 2007 Chrystler 300 96
6 83 2014 Honda Accord 112
7 65 2009 Honda Fit 106
8 31 2007 Honda Fit 102
10 128 2010 Infiniti G37 128
11 124 2015 Jeep Wrangler 124
12 91 2010 Mitsu Lancer 102
13 126 2010 Volkswagen Eos 92
请注意,这与您的预期输出不同:Make
按字母顺序排序(Audi
在 Acura
之后,而不是 Honda
)并且只有 3保留最大的 Total
(112, 106, 102
用于 Honda
,而不是 112, 102, 92
)。
如果您使用 GNU awk
,并且您的输入文件足够小以适合内存,您也可以只用 awk
完成所有这些,这要归功于它的多维数组和它的 asorti
函数,根据索引对数组进行排序:
$ awk -F'\t' -vOFS='\t' 'NR==1 {print; next} {l[][][[=11=]]}
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
for(m in l) {
n = asorti(l[m], t, "@ind_num_desc"); n = (n>3) ? 3 : n
for(i=1; i<=n; i++) for(s in l[m][t[i]]) {[=11=] = s; = ++r; print}
}
}' data.tsv
Ranking ID Year Make Model Total
1 113 2012 Acura Tsx sportwagon 116
2 112 2008 Acura TL 110
3 50 2015 Acura TLX 102
4 15 014 Audi S4 120
5 216 2007 Chrystler 300 96
6 83 2014 Honda Accord 112
7 65 2009 Honda Fit 106
8 31 2007 Honda Fit 102
9 128 2010 Infiniti G37 128
10 124 2015 Jeep Wrangler 124
11 91 2010 Mitsu Lancer 102
12 126 2010 Volkswagen Eos 92
对数组的数组使用 GNU awk 和 sorted_in
:
$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR == 1 {
print
next
}
{
rows[][][++numRows[,]] = [=10=]
}
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
for ( make in rows ) {
PROCINFO["sorted_in"] = "@ind_num_desc"
cnt = 0
for ( total in rows[make] ) {
for ( rowNr=1; rowNr<=numRows[make,total]; rowNr++ ) {
if ( ++cnt <= 3 ) {
row = rows[make][total][rowNr]
print row, cnt
}
}
}
}
}
$ awk -f tst.awk file
Ranking ID Year Make Model Total
4 113 2012 Acura Tsx sportwagon 116 1
6 112 2008 Acura TL 110 2
9 50 2015 Acura TLX 102 3
3 15 014 Audi S4 120 1
11 216 2007 Chrystler 300 96 1
5 83 2014 Honda Accord 112 1
7 65 2009 Honda Fit 106 2
10 31 2007 Honda Fit 102 3
1 128 2010 Infiniti G37 128 1
2 124 2015 Jeep Wrangler 124 1
8 91 2010 Mitsu Lancer 102 1
12 126 2010 Volkswagen Eos 92 1
上面的代码将处理 1 个品牌的多辆汽车总数相同的情况,方法是始终只打印该品牌的前 3 行,例如鉴于此输入,其中 4 个 Acuras 总共有 116 个:
$ cat file
Ranking ID Year Make Model Total
1 128 2010 Infiniti G37 128
2 124 2015 Jeep Wrangler 124
3 15 014 Audi S4 120
4 113 2012 Acura Tsx sportwagon 116
4 113 2012 Acura Foo 116
4 113 2012 Acura Bar 116
4 113 2012 Acura Other 116
5 83 2014 Honda Accord 112
6 112 2008 Acura TL 110
7 65 2009 Honda Fit 106
8 91 2010 Mitsu Lancer 102
9 50 2015 Acura TLX 102
10 31 2007 Honda Fit 102
11 216 2007 Chrystler 300 96
12 126 2010 Volkswagen Eos 92
13 13 2016 Honda Civic 1.5t 92
这是仅显示 4 116 辆 Acuras 中的 3 辆的输出:
$ awk -f tst.awk file
Ranking ID Year Make Model Total
4 113 2012 Acura Tsx sportwagon 116 1
4 113 2012 Acura Foo 116 2
4 113 2012 Acura Bar 116 3
3 15 014 Audi S4 120 1
11 216 2007 Chrystler 300 96 1
5 83 2014 Honda Accord 112 1
7 65 2009 Honda Fit 106 2
10 31 2007 Honda Fit 102 3
1 128 2010 Infiniti G37 128 1
2 124 2015 Jeep Wrangler 124 1
8 91 2010 Mitsu Lancer 102 1
12 126 2010 Volkswagen Eos 92 1
如果这不是您想要的,则将 if ( ++cnt <= 3 )
测试移至外循环或按您希望的方式处理它。