如何根据第一行对列进行排序?


12

我需要对一个非常大的数据集的列进行排序(1000行和700000列)。例如,我的列是随机排列的,例如:col1 col4 col3 col2,我需要对其进行排序。

我一直在尝试一些命令,但没有成功。

例:

ID M2 M5 M8 M1 M3 M9 .....M7000000
Animal1 1 0 2 1 0 2 .....1
Animal2 0 1 2 0 1 1 .....0
Animal3 2 1 0 1 2 1 .....0
.
.
.
.
Animaln

在此示例中,点表示我有很多列和线。同样,我需要对列进行排序,例如:

ID M1 M2 M3 M4 M5 M6 .....M7000000
Animal1 1 0 2 1 0 2 .....1
Animal2 0 1 2 0 1 1 .....0
Animal3 2 1 0 1 2 1 .....0
.
.
.
.
Animaln

谢谢


您能否添加一个包含几行数据集的示例?
jcbermu

您的预期结果仅对第一行进行了排序,其他值保持不变,为什么?
RomanPerekhrest

实际上,它需要遵循专栏,这是一个示例错误。抱歉
LLVerardo 2017年

需要根据第一行对整个列进行排序。
LLVerardo

2
转置,按第一列排序,再转回。
佐藤桂

Answers:


10

使用GNU datamash和GNU sort

datamash transpose -t ' ' -H <file_in.csv | sort -V | datamash transpose -t ' ' -H >file_out.csv

这对于“合理小”的数据很好用。它可能会或可能不会与您的文件一起使用。

编辑:下面没有换位的解决方案应该减少资源密集型。


1
RS命令可能是用一种较轻的替代datamash例如rs -T < file_in.csv | sort | rs -T -C' 'rs应该作为对基于Debian的系统封装)
steeldriver

2
FWIW rs(“重塑数据阵列”)在某些BSD的基本系统中可用。
库萨兰达

6
perl -pale '
   $. == 1 and
   @I = map  { $_->[1] }
        sort { $a->[0] <=> $b->[0] }
        map  { [ $F[$_] =~ /^M(\d+)$/, $_ ] } 1..$#F;
   $_ = "@F[0, @I]";
' yourlargefile

  1. 对于第一行,我们M使用众所周知的数字,使用在开始出现的数字之后的数字部分对其第二...最后一列进行数字排序Schwartzian maneuver。这为我们提供了重新排序的索引,以便列按数字排序的顺序出现(M1,M2,M3,...)
  2. 剩下的就是使用这些来自的索引来@I重新排列@F元素。
  3. 以双引号形式分配数组会将其转换为元素空间分隔的字符串。
  4. -pPerl的选项启用$_内容的自动打印,-l应添加newline

6

使用perl模块Sort :: Naturally

输入数据

ID M2 M5 M8 M1 M3 M9 M700000
A1 m1,2 m1,5 m1,8 m1,1 m1,3 m1,9 m1,7000000
A2 m2,2 m2,5 m2,8 m2,1 m2,3 m2,9 m2,7000000
A3 m3,2 m3,5 m3,8 m3,1 m3,3 m3,9 m3,7000000
A1000 m1000,2 m1000,5 m1000,8 m1000,1 m1000,3 m1000,9 m1000,7000000
perl -MSort::Naturally -lane '
  if ($. == 1) {
    @indices = (0, map  { $_->[0] }
                   sort { ncmp($a->[1], $b->[1]) }
                   map  { [$_, $F[$_]] }
                   1..$#F
               );
    $, = " ";
  }
  print @F[@indices]
' test.data

输出

ID M1 M2 M3 M5 M8 M9 M700000
A1 m1,1 m1,2 m1,3 m1,5 m1,8 m1,9 m1,7000000
A2 m2,1 m2,2 m2,3 m2,5 m2,8 m2,9 m2,7000000
A3 m3,1 m3,2 m3,3 m3,5 m3,8 m3,9 m3,7000000
A1000 m1000,1 m1000,2 m1000,3 m1000,5 m1000,8 m1000,9 m1000,7000000

+1(最优雅),不假定列名的前缀太具体,这是一种解决方法。
arielf

4

如果您安装了该rs实用程序,则可以执行以下操作:

rs -c' ' -T | {
    stdbuf -i0 sed "1q"
    sort -V
} | rs -C' ' -T

或全部一行:

rs -c' ' -T | { stdbuf -i0 sed "1q"; sort -V ; } | rs -C' ' -T
  • 第一个rs转置输入数据(带有空格的字段)
  • 命令组:
    • sed读取第一行,将其输出,然后退出,使其余管道rs保持原样。 stdbuf需要sed通过关闭输入缓冲来确保仅读取第一个换行符,而不读取更多内容
    • sorts剩余的行
  • 第二个rs将结果流转置为其原始格式。

rs在MacOS上默认安装。在Linux系统上,您可能必须安装它-例如

sudo apt install rs

警告:stdbufsorts -V选项是特定于GNU的,因此无法在未修改的MacOS上使用。


0

如果您具有GNU awk,则可以尝试以下操作:

NR == 1 {
    for (i = 2; i <= NF; i++) {
        columns[substr($i, 2)] = i;
    }
    count = asorti(columns, sorted, "@ind_num_asc");
    printf("%s", $1);
    for (i = 1; i <= count; i++) {
        printf(" M%s", sorted[i]);
        indx[i] = columns[sorted[i]];
    }
    print "";
    next;
}
{
    printf("%s", $1);
    for (i = 1; i <= count; i++) {
        printf(" %s", $(indx[i]));
    }
    print "";
}

0

在Python中:

from csv import DictReader, DictWriter
with open('in_file.csv') as infile, open('out_file.csv') as outfile:
  reader = DictReader(infile)
  writer = DictReader(outfile, fieldnames=sorted(reader.fieldnames))
  writer.writerows(reader)

0

我不知道您是否认为这是一个好答案,但是...

为什么不使用数据库解决此问题?您可以将数据集导入为临时表,然后执行

从my_temp_table中选择Column1,column2,... column-n

您可以根据需要使用其他过滤器或转换。然后,您可以根据需要重新格式化输出。

所有这些任务都可以编程为bash脚本,并使用管道链接输出。

有时我已经使用过“ pv”命令来查看命令之间的输出进度。

要导入数据集,您可以使用Pentaho Data Integration对ETL进行编程。


0

也许这也可以帮助您。

  1. 首先,您可以使用转置文件(/programming/1729824/an-ficient-way-to-transpose-a-file-in-bash中的一个)
  2. 使用sort命令对第一列进行排序。
  3. 再次移调。

例如:

$ echo "ID M2 M5 M8 M1 M3 M9 .....M7000000
Animal1 1 0 2 1 0 2 .....1
Animal2 0 1 2 0 1 1 .....0
Animal3 2 1 0 1 2 1 .....0
.
.
.
.
Animaln" | awk '
{ 
    for (i=1; i<=NF; i++)  {
        a[NR,i] = $i
    }
}
NF>p { p = NF }
END {    
    for(j=1; j<=p; j++) {
        str=a[1,j]
        for(i=2; i<=NR; i++){
            str=str" "a[i,j];
        }
        print str
    }
}' | sort -n | awk '
{ 
    for (i=1; i<=NF; i++)  {
        a[NR,i] = $i
    }
}
NF>p { p = NF }
END {    
    for(j=1; j<=p; j++) {
        str=a[1,j]
        for(i=2; i<=NR; i++){
            str=str" "a[i,j];
        }
        print str
    }
}'
ID M1 M2 M3 M5 .....M7000000 M8 M9
Animal1 1 1 0 0 .....1 2 2
Animal2 0 0 1 1 .....0 2 1
Animal3 1 2 2 1 .....0 0 1
.       
.       
.       
.       
Animaln    
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.