【问题标题】:overlay rows based on common values from sequential rows [closed]基于来自连续行的公共值覆盖行[关闭]
【发布时间】:2013-08-21 09:53:48
【问题描述】:

我有这样的输入:

A  200-400  213  253  295  350  0011
A  200-400  260  295  315  000
A  200-400  205  263  295  111
B  800-900  801  832  840  843  870  890  895  00110101
B  800-900  801  823  850  010
B  800-900  850  1
.
.
.

最后一列的0和1值对应第三列到最后一列的值

我想生成一个制表符分隔的矩阵,如下所示:

A 200-400  NA   213  253  NA   NA   295  NA   350 
A 200-400  NA   NA   NA   260  NA   295  315  NA
A 200-400  205  NA   NA   NA   263  295  NA   NA 
B 800-900  801  NA   832  840  843  NA   870  890 895 900
B 800-900  801  823  NA   NA   NA   850  NA   NA  NA  NA
B 800-900  NA   NA   NA   NA   NA   850  NA   NA  NA  NA

最后将0和1的值替换为对应的值和

A  200-400  NA  0    0    NA   NA   1    NA    1 
A  200-400  NA  NA   NA   0    NA   0    0     NA
A  200-400  1   NA   NA   NA   1    1    NA    NA 
B  800-900  0   NA   0    1    1    NA   0     1   0   1
B  800-900  0   1    NA   NA   NA   0    NA    NA  NA  NA
B  800-900  NA  NA   NA   NA   NA   1    NA    NA  NA  NA

非常感谢您的帮助。

【问题讨论】:

  • 你试过什么?请注意,您发布了一个相关问题stackoverflow.com/questions/18273941/…,您可以重复使用部分代码。
  • 有人有魔杖吗?
  • 你是如何在这里填写 NA 的?请更正标题中的错字。
  • @fedorqui 我不知道怎么写。我正在学习 perl 和 R,但距离太远,无法编写。
  • 我可以看到00100 模式和NAs 分布之间的no 联系。请制定一个明确的算法来解释您要做什么。

标签: r perl awk


【解决方案1】:

多么有趣的问题。我将在 Perl 中回答。

我们需要一次读取同一范围内的所有行。这些范围内的每个数字还必须记住它们来自哪一行。然后,我们可以对每个范围的数字进行排序,并重新组合行。

对于第一个范围,我们将有一个值的集合,例如

[213 => 1], [253 => 1], [295 => 1], [350 => 1],
[260 => 2], [295 => 2], [315 => 2],
[205 => 3], [263 => 3], [295 => 3],

我们应该去重复常见的数字,这样我们得到

[213 => 1], [253 => 1], [295 => 1, 2, 3], [350 => 1],
[260 => 2], [315 => 2],
[205 => 3], [263 => 3],

(顺序不重要)。

我们可以按第一个字段对这些项目进行排序:

my @sorted = sort { $a->[0] <=> $b->[0] } @items;

对于每一行,我们可以遍历已排序的项目,并根据行号决定是否打印NA 或数字:

for my $line (1 .. 3) {
  my @fields = map { decide_if_number_or_na($line, @$_) } @sorted;
  ...
}

sub decide_if_number_or_na {
  my ($line, $number, @lines) = @_;
  return $number if grep { $line == $_ } @lines;  # ... if any of the lines is our line
  return "NA";
}

当然,我们应该立即发出正确的01 值。

将所有这些联系在一起有点复杂。在解析输入的过程中,我们需要将每一行与当前的01 模式相关联,记住前两个字段,并为项目构建数据结构。

生成的代码遵循上述考虑,但采取了一些捷径:一旦它们被排序,每个数字的实际值对我们的项目并不重要,我们可以丢弃它。

use strict; use warnings; use feature 'say';

my @lines;   # an array of hashes, which hold information about each line
my %ranges;  # a hash mapping range identifiers to number-to-occuring-line-array hashes

while (<>) {
  chomp;
  my ($letter, $range, @nums) = split;  # split everything into field ...
  my @pattern = split //, pop @nums;    # but the last field is a pattern, which we split into chars.
  push @{ $ranges{$range}{$_} }, $. for @nums;  # $. is the line no
  push @lines, {
    letter  => $letter,
    range   => $range,
    pattern => \@pattern,
    line    => $.,
  };
}

# simplify and sort the ranges:
for my $key (keys %ranges) {
  my $nums2lines = $ranges{$key};  # get the number-to-occuring-lines-array hashes
  # read the next statement bottom to top:
  my @items =
    map { $nums2lines->{$_} }  # 3. get the line number arrayref only (forget actual number, now that they are ordered)
    sort { $a <=> $b }         # 2. sort them numerically
    keys %$nums2lines;         # 1. get all numbers
  $ranges{$key} = \@items; # Remember these items at the prior position
}

# Iterate through all lines
for my $line (@lines) {
  # Unpack some variables
  my @pattern = @{ $line->{pattern} };
  my $lineno  = $line->{line};
  my $items   = $ranges{$line->{range}};

  # For each item, emit the next part of the pattern, or NA.
  my @fields  = map { pattern_or_na($lineno, @$_) ? shift @pattern : "NA" } @$items;
  say join "\t", $line->{letter}, $line->{range}, @fields;
}

sub pattern_or_na {
  my ($line, @lines) = @_;  # the second value (the specific number)
  return scalar grep { $_ == $line } @lines;  # returns true if a number is on this line
}

这会产生所需的输出。

这是相当复杂的代码,尤其是对于初学者。它利用 Perl 引用和 autovivifiction。此外,我使用了许多列表转换,例如 sortmapgrep。该解决方案没有考虑到具有相同范围的行是连续的,因此我不必将所有内容都保存在内存中。这个解决方案更简单(原文如此!),但使用的内存比必要的多。

我建议阅读 perlreftutperlreperldsc 手册页以了解所有这些内容。

【讨论】:

    猜你喜欢
    • 2017-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 2014-07-16
    • 1970-01-01
    • 2023-03-25
    相关资源
    最近更新 更多