【问题标题】:2d histogram making二维直方图制作
【发布时间】:2012-01-05 13:16:57
【问题描述】:

我有一个包含两列的数据文件,比如

1.1 2.2
3.1 4.5
1.2 4.5
3.2 4.6
1.1 2.3
4.2 4.9
4.2 1.1

我想从两列中制作一个直方图,即得到这个输出(如果步长(或 bin 大小,正如我们所说的直方图)等于 0.1 在这种情况下)

1.0 1.0 0
1.0 1.1 0
1.0 1.2 0
...
1.1 1.0 0
1.1 1.1 0
1.1 1.2 0
...
1.1 2.0 0
1.1 2.1 0
1.1 2.2 1
...
...

谁能给我一些建议?如果我可以设置 colmuns 的值范围,那就太好了。在上述情况下,第一列的值从 1 变为 4,与第二列相同。

已编辑: 已更新以处理更一般的数据输入,例如浮点数。上述情况下的步长为 0.1,但如果它可以针对其他设置进行调整会更好,即步长范围(bin size)例如为 0.2 或 1.0。 例如,如果步长为 1.0,那么如果我有 1.1 和 1.8,它们具有相同的 bin,我们必须一起处理它们,例如(在这种情况下,范围让我们说 4 两列 0.0 .. . 4.0)

1.1 1.8
2.5 2.6
1.4 2.1
1.3 1.5
3.3 4.0
3.8 3.9
4.0 3.2
4.0 4.0

输出(如果 bin 大小 = 1.0

1 1 2
1 2 1
1 3 0
1 4 0

2 1 0
2 2 1
2 3 0
2 4 0

3 1 0
3 2 0
3 3 1
3 4 1

4 1 0
4 2 0
4 3 1
4 4 1

【问题讨论】:

  • 4 1的直方图不应该是1吗?
  • 是的,当然,我做错了,对不起...

标签: linux bash shell awk


【解决方案1】:
awk 'END {
  for (i = 0; ++i <= l;) {
    for (j = 0; ++j <= l;)
      printf "%d %d %d %s\n", i, j, \
        b[i, j], (j < l ? x : ORS) 
    }
  }
{
  f[NR] = $1; s[NR] = $2
  b[$1, $2]++
  }' l=4 infile

你可以试试这个(未经彻底测试):

awk -v l=4 -v bs=0.1 'BEGIN {
  if (!bs) {   
   print "invalid bin size" > "/dev/stderr"
   exit
    }
  split(bs, t, ".")
  t[2] || fl++
  m = "%." length(t[2]) "f" 
  }
{
  fk = fl ? int($1) : sprintf(m, $1)
  sk = fl ? int($2) : sprintf(m, $2)
  f[fk]; s[sk]; b[fk, sk]++
  }

END {
  if (!bs) exit 1

  for (i = 1; int(i) <= l; i += bs) {
    for (j = 1; int(j) <= l; j += bs) {
      if (fl) {
        fk = int(i); sk = int(j); m = "%d"
        }
      else {
        fk = sprintf(m, i); sk = sprintf(m, j)
        }     
      printf "%s" m OFS m OFS "%d\n", (i > 1 && fk != p ? ORS : x), fk, sk, b[fk, sk]
      p = fk        
      }
    }
  }'  infile

【讨论】:

  • 嗨,很好!如何以能够使用浮点数的方式对其进行调整,例如 1.45、3.1230、2.101 等。 ?
  • 嗨@user1116360,您使用的是哪个awk 版本?解决方案将取决于您的 awk 实现。
  • 嗨 Dimitre,awk 版本:Ubuntu 10.04 LTS 上的 GNU awk 3.1.6
  • 嗨@user1116360,您能否发布示例数据和带有浮点数的所需输出示例?我的意思是,如果您在第一列中有 1、1.2、3,那么范围应该是多少:1、1.2、2、3 或 1、1.2、2、2.2、3、3.2 ...?
【解决方案2】:

你可以在 bash 中试试这个:

for x in {1..4} ; do
    for y in {1..4} ; do
        echo $x%$y 0
    done
done \
| join -1 1 -2 2 - -a1 <(sed 's/ /%/' FILE \
                         | sort \
                         | uniq -c \
                         | sort -k2 ) \
| sed 's/ 0 / /;s/%/ /'

它创建最后一列全为零的表,将其与实际结果(经典频率表sort | uniq -c)连接起来,并从应该显示不同数字的行中删除零。

【讨论】:

  • 很复杂,fgrep 可以更轻松地处理这个问题,看我的回答
  • @fge:我见过。您多次调用 fgrep 这会使脚本变慢以处理较长的表。
  • 好吧,你调用 sed,然后加入,然后排序,然后 uniq,然后排序,然后再次 sed ;)
  • @fge:是的。就是这样,6 需要一张任意大小的桌子。即使是 4x4,您也有 4*4=16 次 fgrep 调用。
【解决方案3】:

perl 中的一种解决方案(示例输出和用法如下):

#!/usr/bin/perl -W
use strict;

my ($min, $step, $max, $file) = @ARGV
    or die "Syntax: $0 <min> <step> <max> <file>\n";

my %seen;

open F, "$file"
    or die "Cannot open file $file: $!\n";

my @l = map { chomp; $_}  qx/seq $min $step $max/;

foreach my $first (@l) {
    foreach my $second (@l) {
        $seen{"$first $second"} = 0;
    }
}

foreach my $line (<F>) {
    chomp $line;
    $line or next;
    $seen{$line}++;
}

my $len = @l; # size of list
my $i = 0;

foreach my $key (sort keys %seen) {
    printf("%s %d\n", $key, $seen{$key});
    $i++;
    print "\n" unless $i % $len;
}

exit(0);

【讨论】:

  • 如果我有“浮动”数字,1.4 5.4 23.14,...等怎么办?我们可以按照这个要求来调优吗?
  • 正如我所说,这与您的数据非常相关,如果您的数据集比这更一般,您可以编辑您的问题以描述整个问题集吗?*
  • 好的,删除我的答案,因为如果没有描述整个问题集,我将无能为力。
  • 修复后,它应该完全满足您的需求
  • 谢谢,fge!还有一个更简单的问题:我想(通常)在每个新的外部 for 循环开始之后添加一个新行;例如,在这些行 1 4 1, 2 4 0, ...
猜你喜欢
  • 2015-05-29
  • 1970-01-01
  • 1970-01-01
  • 2020-07-07
  • 2012-10-20
  • 1970-01-01
  • 2020-12-04
  • 1970-01-01
  • 2021-10-13
相关资源
最近更新 更多