【问题标题】:Randomly sample N lines from two text files(parallel corpus) consistently从两个文本文件(并行语料库)中随机采样 N 行
【发布时间】:2018-08-08 19:13:47
【问题描述】:

一次又一次,我面临从两个文件(即来自一个并行语料库)中采样特定行数(比如说N)的问题;句子是根据到行号)始终

对于从事(神经)机器翻译研究的任何人来说,这是一项常见且频繁的任务。

我想知道一种快速有效的方法来从并行语料库中采样(即选择)N 行,可能来自命令行。

例如,如果我们想以一致的方式从两个文件中选择4 行,我们可以在行号3121723 处采样行。这应该为我们提供两个文件中的这些行。此外,最好将此参数N 设为任意参数,以便我们可以随意更改它。此外,应不重复地对这些行进行采样。而且,需要采样的所需行数N 将始终少于两个文件中的总行数,其中两个文件的总行数始终相同。

一旦我们对所需的行进行了采样,还希望从两个文件中获取尚未采样的行(即获取随机采样中未选择的其余行)。

这样做的整个想法是以一致的方式对两个文件进行采样,以便保留它们的行对齐。 (即选择N 行和N-T 行,其中T 是总行数。)

其中N 是不重复采样的所需行数,N-T 是未采样的其余行。

我该怎么做呢?提前致谢!

【问题讨论】:

  • 重复还是不重复?可以对同一行进行多次采样。是否还考虑到文件行大小大于 N?
  • @karakfa 无需重复!需要采样的所需行数N 将始终少于两个文件中的总行数:)。此外,两个文件的行数相同。谢谢!

标签: linux bash shell file command-line


【解决方案1】:

如果不允许重复,最好使用随机播放算法。已经有一个工具shuf 用于此目的。

例如,

$ shuf -n 10 file

会从文件中随机选择 10 行(以随机顺序)。您的请求有两个额外的约束,首先应该对选择进行排序,其次选择需要在另一次运行中保持一致。对于第二个要求,您可以向shuf 提供随机源以两次获得相同的序列。对于排序,我们靠自己...

$ shuf -n 10 --random-source=file <(cat -n file1) | sort -n | cut -f2- > sample1
$ shuf -n 10 --random-source=file <(cat -n file2) | sort -n | cut -f2- > sample2

将以正确的顺序为您提供相同的采样行。对于随机性,您可以使用任何一个文件或任何其他第三个文件(但两次运行应该相同)。

另一种选择是将两个文件粘贴在一起,然后进行一次随机播放,然后拆分样本。

$ paste -d'|' file1 file2 | cat -n | shuf -n 10 | sort -n | cut -f2 > sample

$ cut -d'|' -f1 sample > sample1
$ cut -d'|' -f2 sample > sample2

要获得未选择的行,您需要保留行号。使用第二种选择

$ paste -d'|' file1 file2 | cat -n | shuf -n 10 | sort -n > n_samples
$ cut -f2- n_samples > samples
$ awk 'NR==FNR{a[$1];next} !(FNR in a)' <(cut -f1 n_samples) samples > notselected

您可以像以前一样拆分样本和未选择的文件。

使用第一种替代方法,文件中未选择的行将被写入具有相同名称和扩展名“.not”的文件

$ cat -n file1 | shuf -n 10 --random-source=file | sort -n > n_sample1
$ cut -f2- n_sample1 > sample1
$ cat -n file2 | shuf -n 10 --random-source=file | sort -n | cut -f2- > sample2
$ awk 'NR==FNR    {a[$1];next} 
      !(FNR in a) {print > FILENAME".not"}' <(cut -f1 n_sample1) sample1 sample2 

【讨论】:

  • 谢谢!有没有一种简单的方法也可以从两个未采样的文件中获取行?即获取在随机抽样中未被选择的其余行
  • 谢谢!我发现第一种方法比第二种方法更直接。是不是可以使用第一种方法获得未选择的行?
  • 是的,做同样的事情;保存sort -n 之后的行号样本并提取行号,如图所示。你只需要一次,因为两个行号是相同的。
  • 为了完整起见,您介意在答案中添加这个吗? :)
  • 已更新但再次未经测试。请在此处验证并发表评论。
【解决方案2】:
#!/bin/bash
samples=$1
file1="$2"
file2="$3"

maxlines=$(cat "$file1" | wc -l)
nums=($(shuf -e $(echo $(seq 1 $maxlines))))
lines=$(for i in $(seq 1 $samples); do echo ${nums[$i]}p" "; done | sort -n)
sed -n "$lines" "$file1"
sed -n "$lines" "$file2"

#rows1=($(sed -n "$lines" "$file1"))
#rows2=($(sed -n "$lines" "$file2"))
  • 参数 $1,2,3:样本数,文件名 1 和 2。
  • 带有 wc -l 和 cat 的 maxline,因为这样我们就不需要删除文件名。 (这里没有无用的猫奖)。
  • seq x y 生成从 x 到 y 的序列,这里是 1 到 maxlines。没有差一错误,因为源 wc 和 sed 一样(稍后)从 1 开始计数。
  • shuf -e 随机排列数字,而不是 -e 期望它们按行排列
  • 对于样本计数,从前面拉行号,
    • 但是对它们进行排序,这样 sed 只需遍历文件一次。
    • 后缀“p”用于 sed。
  • sed -n,对于一个 11 行的小文件,sample=4,看起来像这样:

    sed -n 3p 4p 5便士 7p mul.sh

如果您不想按文件输出,而是按行输出,请将它们收集到数组中:

rows1=($(sed -n "$lines" "$file1"))
rows2=($(sed -n "$lines" "$file2"))

这样,sed 仍然只需要遍历可能较大的文件一次。使用从 0 到 $sample-1 的数组索引,您可以遍历两个行数组并比较它们 - 或者任何工作。

【讨论】:

    猜你喜欢
    • 2018-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-22
    • 2015-09-02
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    相关资源
    最近更新 更多