【问题标题】:Shuffle the rows of specific columns of table in Perl在 Perl 中打乱表格特定列的行
【发布时间】:2021-03-07 15:11:21
【问题描述】:

我有一个表格文件,我想在 Perl 中打乱特定列的行。

例如,我有这个数组:

a 1
b 2
c 3
d 4
e 5
f 6

我想洗牌第二列得到这样的东西:

a 2
b 1
c 3
d 4
e 5
f 6

【问题讨论】:

    标签: csv perl row shuffle


    【解决方案1】:

    我可以使用这个脚本来完成这项工作:

    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    
    use List::Util qw/shuffle/;
    
    my @c = split /,/, $ARGV[0];
    $_-- for @c;
    shift;
    
    my @lines;
    my @v;
    while ( <> ) {
        my @items = split;
        $v[$.-1] = [@items[@c]];
        $lines[$.-1] = [@items];
    }
    
    my @order = shuffle (0..$#lines);
    
    for my $l (0..$#lines) {
        my @items = @{ $lines[$l] };
        @items[@c] = @{ $lines[$order[$l]] }[@c];
        print "@items\n";
    }
    

    此脚本使用List::Util,它是自 perl v5.7.3 以来 Perl 核心模块的一部分:corelist List::Util

    可以用perl shuffle.pl 2 test.txt启动

    【讨论】:

    • 感谢您指出这一点。我知道 Perl 不是唯一使用 0 作为数组第一个索引的语言,但我觉得这很不自然,尤其是 Perl 不是 C,而且数组不仅仅是指向内存中插槽的指针。
    • 我不知道,新的 Perl 版本到了,但是 Unix 系统中使用的默认版本已经很长时间没有改变了,我不确定它是否会很快发生。
    • 我不知道为什么上面其他人的评论被删除了,但我更仔细地查看了有关 $[ (perldoc.perl.org/perlvar#$%5B) 的文档,我发现它开始不再有效从 Perl v5.30.0 开始,即使没有做 use v5.16,所以我不得不从我的脚本中删除 $[=1 的使用 :(.
    【解决方案2】:

    不允许使用外部模块时的示例代码。

    use strict;
    use warnings;
    use feature 'say';
    
    my %data;
    
    while( <DATA> ) {
        my($l,$d) = split;
        $data{$l} = $d;
    }
    
    say '- Loaded --------------------';
    say "$_ => $data{$_}" for sort keys %data;
    
    for( 0..5 ) {
        @data{ keys %data} = @{ shuffle([values %data]) };
        say "-- $_ " . '-' x 24;
        say "$_ => $data{$_}" for sort keys %data;
    }
    
    sub shuffle {
        my $data = shift;
        
        my($seen, $r, $i);
        my $n = $#$data;
        
        for ( 0..$n ) {
            do {
                $i = int(rand($n+1));
            } while defined $seen->{$i};
            $seen->{$i} = 1;
            $r->[$_] = $data->[$i];
        }
        
        return $r;
    }
    
    __DATA__
    a 1
    b 2
    c 3
    d 4
    e 5
    f 6
    

    输出

    - Loaded --------------------
    a => 1
    b => 2
    c => 3
    d => 4
    e => 5
    f => 6
    -- 0 ------------------------
    a => 5
    b => 4
    c => 2
    d => 6
    e => 1
    f => 3
    -- 1 ------------------------
    a => 3
    b => 6
    c => 2
    d => 4
    e => 1
    f => 5
    -- 2 ------------------------
    a => 4
    b => 5
    c => 6
    d => 1
    e => 3
    f => 2
    -- 3 ------------------------
    a => 6
    b => 4
    c => 1
    d => 2
    e => 3
    f => 5
    -- 4 ------------------------
    a => 3
    b => 4
    c => 6
    d => 5
    e => 1
    f => 2
    -- 5 ------------------------
    a => 6
    b => 5
    c => 3
    d => 4
    e => 2
    f => 1
    

    【讨论】:

    • 不能使用哈希键来存储表数据。如果有重复怎么办?您甚至不能保留第 1 列的原始顺序,因为散列没有排序。仅仅因为它们在样本数据中碰巧是唯一且可排序的,并不意味着它们会出现在真实场景中。带有等待空闲数组索引的 while 循环的 shuffle 函数对于较大的数组来说效率极低。想象一个有 10000 行的文件,取了 9999 个数字,每次滚动都有 1/10000 的机会命中最后一个数组索引。
    • @TLP - OP 应该更好地描述预期输入数据的问题。您的观点在索引计算算法上是有效的。哈希仅用于演示目的,可以实现两个数组来存储元素。
    • 我在问题中提供的表格只是一个示例,我希望脚本能够处理任何表格文本文件作为输入。
    • 另请注意,您提出的洗牌算法不是最优的,因为找到最后一个索引可能需要很多时间。查看它在 List::Util 中的实现方式,其中复杂性降低到 O[n]:stackoverflow.com/a/5168324/4374441
    • @JeanPaul -- 如果由于安全原因,系统中不允许安装任何模块,您会怎么做。您是否为您的表输入了 UTF-8 键和值?至少您必须更详细地描述问题。没有一种解决方案可以适应所有可能的情况(这种解决方案的规模呈指数增长并且变得低效)。
    【解决方案3】:

    使用List::Util::shuffle 可能是个好主意。我使用Schwartzian transform 创建了一个随机数列表,对它们进行排序,然后根据数组索引插入列数据。

    use strict;
    use warnings;
    use feature 'say';
    
    my @col;
    while (<DATA>) {
        push @col, [ split ];
    }
    my @shuffled = map { $col[$_->[0]][1] }      # map to @col values
                   sort { $a->[1] <=> $b->[1] }  # sort based on rand() value
                   map { [ $_, rand() ] }        # each index mapped into array of index and rand()
                   0 .. $#col;                   # list of indexes of @col
    for my $index (0 .. $#col) {
        say join " ", $col[$index][0], $shuffled[$index];
    }
    __DATA__
    a 1
    b 2
    c 3
    d 4
    e 5
    f 6
    

    【讨论】:

    • 不错的解决方案。它使用的排序可能比简单的随机播放更占用 CPU 资源(我认为它是 O[n²] vs O[n]),但它可以工作。该脚本还需要适应任何表格文本文件。
    • @JeanPaul 谢谢,它主要只是展示了 Schwartzian 变换的酷炫程度。
    猜你喜欢
    • 1970-01-01
    • 2016-10-04
    • 2017-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-03
    • 2013-07-12
    相关资源
    最近更新 更多