从文件中随机选择可能重复的n行

时间:2012-04-21 20:14:03

标签: linux perl bash

我有一个超过100万行的文本文件。各行不是很大(每个约200-270个字符)。

我试图随机选择输入中60%的行数,其中每行可以在输出中重复。在上面的示例中,我的输出将有600,000行,但只有500,000行可能是唯一的。我还需要在不同的输出文件中完全没有拾取的行。任何单独的行都不应出现在两个输出文件中。

输入文件中的每一行都有如下记录。

  • 记录1
  • RECORD2
  • RECORD3
  • RECORD4
  • Record5
  • Record6
  • Record7

如果我试图在文件output1.txt中选择5个随机行,那么每行都可以重复。让我们说下面是选中的行,并在output1.txt

  • RECORD3
  • Record5
  • RECORD2
  • RECORD2
  • Record5

其余记录应转到output2.txt。

  • 记录1
  • RECORD4
  • Record6
  • Record7

记录的顺序无关紧要。

我想我可以使用Java编写代码来执行此操作,但我想知道我可以使用某些命令或脚本快速完成。我尝试使用'shuf'来挑选线条,但是我怎样才能确保拾取的线条没有出现在我想要的第二个输出中。

我正在使用Linux机器。欢迎任何建议或意见。谢谢。

6 个答案:

答案 0 :(得分:2)

这是一个Perl解决方案。

我最近似乎写了很多,但索引一个非常大的文本文件是获取随机访问权限而不将整个文件读入内存的最佳方式。

此程序使用tell运算符在源文件中建立当前记录的偏移量,seek运算符返回到特定记录,并vec跟踪哪些记录已被选中。

请注意,do { ... } while EXPR表单在首次检查条件之前执行do-block,并且是专门为此目的而选择的。

程序希望扫描文件以在命令行上指定数据。对于选定的60%,输出文件为selected.txt,其余为unselected.txt

use strict;
use warnings;

my $file = shift or die "No input file specified";

open my $infh, '<', $file or die qq(Unable to open "$file" for input: $!);
my @index;
do { push @index, tell $infh } while <$infh>;

my $used = "\0" x (@index / 8 + 1);

my $outfh;

open $outfh, '>', 'selected.txt' or die $!;
my $n = 0;
while ($n++ / @index < 0.6) {
  my $rec = int rand scalar @index;
  seek $infh, $index[$rec], 0;
  print $outfh scalar <$infh>;
  vec($used, $rec, 1) = 1;
}

open $outfh, '>', 'unselected.txt' or die $!;
for my $rec (0 .. $#index) {
  next if vec($used, $rec, 1);
  seek $infh, $index[$rec], 0;
  print $outfh scalar <$infh>;
}

修改

我在使用模块替换这么少的代码时犹豫不决,但这里有一个使用Tie::File的版本作为 ikegami 建议,以防有人喜欢这种方法。

use strict;
use warnings;

use Tie::File;

my $file = shift or die "No input file specified";

tie my @index, 'Tie::File', $file, mode => O_RDONLY
    or die qq(Unable to open "$file" for input: $!);

my $outfh;
my @used;

open $outfh, '>', 'selected.txt' or die $!;
my $n = 0;
while ($n++ / @index < 0.6) {
  my $rec = int rand scalar @index;
  print $outfh $index[$rec], "\n";
  $used[$rec]++;
}

open $outfh, '>', 'unselected.txt' or die $!;
for my $rec (0 .. $#index) {
  print $outfh $index[$rec], "\n" unless $used[$rec];
}

答案 1 :(得分:1)

这会随机选取文件的N行中的一行,直到挑选出N / 6行。重复率不受控制。

为了节省内存,我们会将行的文件位置保留在内存中而不是行本身。 Tie::File为我们做到了这一点。

#!/usr/bin/env perl
use strict;
use warnings;

use Tie::File  qw( );

my ($input_qfn, $picked_qfn, $unpicked_qfn) = @ARGV;

tie(my @lines, 'Tie::File', $input_qfn, autochomp => 0)
   or die;

my $num_lines = @lines;
my @unpicked_indexes = 0..$num_lines-1;
my @picked_indexes;
for (1..$num_lines*.6) {
   my $rnd_idx = int(rand($num_lines));
   $unpicked_indexes[$rnd_idx] = undef;
   push @picked_indexes, $rnd_idx;
}

open(my $picked_fh, '>', $picked_qfn)
   or die $!;
print($picked_fh $lines[$_]) for @picked_indexes;

open(my $unpicked_fh, '>', $unpicked_qfn)
   or die $!;
print($unpicked_fh $lines[$_]) for grep defined, @unpicked_indexes;

答案 2 :(得分:0)

示例~STDOUT中10%两次,STDOUT中另外50%,STDERR中剩余40%

awk 'BEGIN {srand()} !/^$/ { r = rand(); if (r <= .60) print $0; if (r <= 0.10) print $0; if (r > .60) print $0 > "/dev/stderr"; }'

注意:将STDOUT重定向到一个文件> file1,将STDERR重定向到其他文件2> file2 ...

答案 3 :(得分:0)

您可以使用以下代码在bash script中执行此操作:

没有重复输出中的行:

#!/bin/bash

lines=$(wc -l inputfile.txt | awk '{print $1}')

echo $lines

# computation of percentage of random lines we
# want to pick e.g. 60%
let percentage=$((lines*60/100))

echo $percentage

# pick the random lines
random_lines=$(sort -R inputfile.txt | head -n $percentage)

# show the random lines
echo $random_lines

输出中重复的行:

#!/bin/bash

lines=$(wc -l inputfile.txt | awk '{print $1}')

echo $lines

# computation of percentage of random lines we
# want to pick e.g. 60%
let percentage=$((lines*60/100))

echo $percentage

# pick the random lines
for ((i=1; i<$percentage; i++))
do
  echo $(sort -R inputfile.txt | head -n 1)
done

答案 4 :(得分:0)

您正在寻找的数学术语如下:您有一组100万个元素,并且您希望选择“替换”样本元素;此外,你想知道未挑选的元素。

universe = range(10**6)  # or whatever your elements are
numElementsToChoose = int(0.6*len(universe))

chosen = [random.choice(universe) for _ in range(numElementsToChoose)]
unchosen = set(universe) - set(chosen)

演示:

>>> len(chosen), len(unchosen)
(600000, 548815)

(这段代码不够优雅,因为universe应该是一个集合,但python本身不支持从集合中挑选一个随机元素,只有一个序列......呃。)

答案 5 :(得分:0)

如果你有shuf,你可能有comm,它有-3选项来比较两个已排序的文件并输出只在一个文件中找到的行。