找到每个唯一$ 1最小$ 2

时间:2016-02-15 15:20:07

标签: bash perl awk sed

我试图获得每1美元价值最小的$ 2值。我的数据如下所示:

0 0
23.9901 13.604
23.9901 13.604
23.9901 3.364
23.9901 3.364
24.054 18.5279
25.0981 17.4839
42.582 0
45.79 0
45.79 15.36
45.7902 12.1518
51.034 12.028
54.11 14.072
54.1102 14.0718

输出必须如下:

0 0
23.9901 3.364
24.054 18.5279
25.0981 17.4839
42.582 0
45.79 0
45.7902 12.1518
51.034 12.028
54.11 14.072
54.1102 14.0718

我可以通过为每个$ 1值创建多个文件并在每个文件中查找min来管理它。但我想知道是否可能有更优雅的解决方案呢?

感谢。

5 个答案:

答案 0 :(得分:3)

使用Gnu或FreeBSD sort,您可以按照以下步骤进行操作;

sort -k1,1 -k2,2g file | sort -k1,1g -su

第一个sort按顺序将文件排序为第一列,然后按第二列值排序。第二个sort仅使用 第一列来确定文件(-u)以确定唯一性。它还使用-s标志来保证第二列仍然有序。在这两种情况下,排序在重要时使用-g标志(见下文),它执行常规数字比较,不像Posix标准-n标志只比较前导整数。

效果说明(感谢OP鼓励我进行测量):

在第一种情况下离开g -k1,1不是拼写错误;它实际上大大加快了排序(在大文件上,使用Gnu排序)。标准或整数(-n)排序比一般数字排序快,可能快10倍。但是,对于“大部分已排序”的文件,所有密钥类型的速度大约是其两倍。对于或多或少均匀采样的随机数,词典排序与一般数字排序非常接近;足够接近,结果显示“大部分已排序”的加速。

可能只能按第一种排序中的第二个字段排序:sort -k2,2g file | sort -k1,1g -su但这很多更慢,因为第一次传递中的主要排序是通用数字而不是lexicographic,因为文件不再主要为第二次传递排序。

这里只是一个示例点,尽管我做了一些测试,结果相似。输入文件由299,902行组成,每行包含0到1,000,000范围内的两个数字,带有三个十进制数字。第一列中只有100,000个不同的数字;每个出现一到五次,第二列中的数字不同。 (第二列中的所有数字都是不同的,因为它发生了。)

所有时间都是用bash的time动词收集的,以真实(挂钟)时间为准。 (很好地排序多线程,因此用户时间总是更长)。

第一列正确排序,第二列随机化:

sort -k1,1  -k2,2g sorted | sort -k1,1g -su          1.24s
sort -k1,1g -k2,2g sorted | sort -k1,1g -su          1.78s
sort        -k2,2g sorted | sort -k1,1g -su          3.00s

第一列随机化:

sort -k1,1  -k2,2g unsorted | sort -k1,1g -su        1.42s
sort -k1,1g -k2,2g unsorted | sort -k1,1g -su        2.19s
sort        -k2,2g unsorted | sort -k1,1g -su        3.01s

答案 1 :(得分:2)

您可以使用此gnu-awk命令:

awk '!($1 in m) || m[$1]>$2{m[$1]=$2} END{for (i in m) print i, m[i]}' file

或者获取与输入文件相同的订单:

awk 'BEGIN{PROCINFO["sorted_in"]="@ind_num_asc"} !($1 in m) || m[$1] > $2 {m[$1] = $2}
     END{for (i in m) print i, m[i]}' file

BEGIN{PROCINFO["sorted_in"]="@ind_num_asc"}用于通过数字索引对关联数组进行排序。

<强>输出:

0 0
23.9901 3.364
24.054 18.5279
25.0981 17.4839
42.582 0
45.79 0
45.7902 12.1518
51.034 12.028
54.11 14.072
54.1102 14.0718

答案 2 :(得分:1)

你可以这样做:

awk 'NR==1{k=$1;v=$2;next} k==$1 { if (v>$2) v=$2; next} {print k,v; k=$1;v=$2}END{print k,v}'

缩进的:

# for the first record store the two fields
NR==1 {
    k=$1
    v=$2
    next
}
# when the first field doesn\'t change
k==$1 {
    # check if the second field is lower
    if (v>$2)
       v=$2
    next
}
{
    # otherwise print stored fields and reinitialize them
    print k,v
    k=$1
    v=$2
}
END {
    print k,v
}'

答案 3 :(得分:1)

Perl:

def self.months
  curr_year = Time.now.year
  (1..12).map do |month|
    month = "#{ curr_year }-#{ month }-01"
    [Date.parse(month).strftime('%Y-%m-%d'), my_select_value]
  end      
end

它是作为Unix过滤器编写的,因此它从STDIN读取并写入STDOUT。称之为:

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

my %min;

while (<>) {
  chomp;
  my ($key, $value) = split;
  if (!exists $min{$key} or $value < $min{$key}) {
    $min{$key} = $value;
  }
}

for (sort { $a <=> $b } keys %min) {
  say "$_ $min{$_}";
}

答案 4 :(得分:0)

如果要使用排序,首先必须修改排序。排序不会理解小数点,因此临时更改x 现在对数字字段进行数字排序并放回小数点。 生成的列表正确排序,取每个键的第一个值。

sed 's/\./ x /g' inputfile | sort -n -k1,3 -k4,6  | sed 's/ x /./g'  | sort -u -k1,1