1

我有一个关于按多列对数据进行排序的问题。我绝对是这方面的初学者,我想知道如何按一列排序,然后再按另一列排序,而不会丢失第一列的顺序。我有一个由三列组成的制表符分隔数据文件。大多数数据不是配对的(一个 id、第一列、位置开始和结束、第二和第三列)。但是,有时,同一个 ID 有多个条目(第一列)。这些需要保持组合在一起(没有空格将它们与下一个条目分开,除非它具有不同的 ID)。数据实际上已经相对于第一列进行了排序,但是我需要根据起始位置(第二列)对其进行数字排序,同时保留原始排序。像这样:

当前格式:

PITG_00129  606 1436

PITG_00130  1   987

PITG_00132  2   1321

PITG_00133 4464 11708
PITG_00133 1 2946
PITG_00133 4081 4515

所需格式:

PITG_00129  606 1436

PITG_00130  1   987

PITG_00132  2   1321

PITG_00133 1 2946
PITG_00133 4081 4515
PITG_00133 4464 11708
4

6 回答 6

5

你可以在 python 中很容易地做到这一点。首先,您需要以正确的格式读取数据:

def line_to_tuple(line):
    data = line.split()
    return (data[0],int(data[1]),int(data[2]))

这会将每一行变成一个按字典顺序排序的元组。由于您的字符串(第一列)以易于排序的方式设置,因此我们无需担心它们。第二列和第三列只需要转换为整数即可使其正确排序。

with open(inputfile) as fin, open(outputfile,'w') as fout:
    non_blank_lines = (line for line in fin if line.strip())
    sorted_lines = sorted(non_blank_lines,key=line_to_tuple)
    fout.writelines(sorted_lines)

这是另一种在字段之间保留空白行的实现:

import itertools
def field1(line):
    data = line.split()
    try:
        return data[0]
    except IndexError:
        return None

def fields(line):
    data = line.split()
    return data[0],int(data[1]),int(data[2])

with open('test.dat') as fin, open('output.dat','w') as fout:
    for k,v in itertools.groupby(fin,key=field1):
        if k is None:
            fout.write('\n')
        else:
            fout.writelines(sorted(v,key=fields))

这使用 itertools 根据空行将文件分块,并在将它们写回之前单独对这些组进行排序。

这是输出:

temp $ cat output.dat 
PITG_00129  606 1436

PITG_00130  1   987

PITG_00132  2   1321

PITG_00133 1 2946
PITG_00133 4081 4515
PITG_00133 4464 11708
于 2013-01-15T21:29:45.160 回答
3

这在 perl 中非常简单,并且总是使用 perl、TIMTOWDI(有不止一种方法可以做到这一点。)下面的两个示例产生了相同的结果。

选项 1 - 在排序中使用或比较运算符。这个例子有一个对象数组。它将首先按last_name 排序,如果它们相等,它将检查first_name。如果您不打算通过不同的变量一遍又一遍地使用数据,这很好。

my @records = (...);

my @sorted = sort { $a->last_name cmp $b->last_name ||
                    $a->first_name cmp $b->first_name } @records;

选项 2 - 使用 sort pragma。Perl 的排序默认情况下是不稳定的,但你可以使用 sort pragma 使其稳定。如果您将通过不同的变量多次使用相同的数据并希望保持上次排序的顺序不变,这是更好的选择。

use sort 'stable';

@records = sort { $a->first_name cmp $b->first_name } @records;

@records = sort { $a->last_name cmp $b->last_name } @records;
于 2013-01-15T22:48:00.850 回答
2

如果你一个接一个地执行排序,并且排序是稳定的,那么第一个排序的顺序将被保留给第二个排序的相等键。我相信Python 的排序功能是稳定的,不确定 Perl。

于 2013-01-15T21:28:52.917 回答
1

算法

 1.  Get the unique first column values
 2.  for each first column value grep the origiginal file and redirect to temp file
       you will get the partial output in temp file for each unique value.  you can use sort command to sort by 2nd column on the temp file and redirect the output (append) to output file. 
于 2013-01-15T21:32:48.090 回答
1

要按第一列和第二列对逗号分隔值列表进行排序,请运行以下命令:

csvfix sort -f 1,2 data.csv

http://csvfix.byethost5.com/csvfix15/csvfix.html

您首先需要查找并替换逗号的选项卡。

于 2013-01-15T21:31:40.803 回答
1

这是一个 Perl 选项,它使用Schwartzian 变换对第一个数值上的聚类线进行排序。转换使用可以轻松排序的键标记要排序的事物。在示例中,map标记所有行。然后该sort块指定如何对这些标记值进行排序:按它们的第二个元素。然后print编辑未标记的值。

use strict;
use warnings;

local $/ = ''; #paragraph mode

while (<>) {
    print $_->[0], "\n"
      for sort { $a->[1] <=> $b->[1] }
          map  { [ $_, (split)[1] ] }
               split /\n/;

    print "\n";
}

调用:$ perl script.pl data.txt > data_sorted.txt

作为单行(perl 5.10 或更高版本):

perl -nE'BEGIN{$/=""}say$$_[0]for sort{$$a[1]<=>$$b[1]}map[$_,(split)[1]],split/\n/;say""' data.txt >data_sorted.txt

数据.txt:

PITG_00129  606 1436

PITG_00130  417   987
PITG_00130  1   987

PITG_00132  2   1321

PITG_00133 4464 11708
PITG_00133 1 2946
PITG_00133 4081 4515

PITG_00134 446 1170
PITG_00134 41 294
PITG_00134 17 451

data_sorted.txt:

PITG_00129  606 1436

PITG_00130  1   987
PITG_00130  417   987

PITG_00132  2   1321

PITG_00133 1 2946
PITG_00133 4081 4515
PITG_00133 4464 11708

PITG_00134 17 451
PITG_00134 41 294
PITG_00134 446 1170
于 2013-01-15T22:47:35.303 回答