【问题标题】:Paste data together in bash将数据粘贴到 bash 中
【发布时间】:2017-11-16 13:25:16
【问题描述】:

我向您展示了一个示例,我需要对我的数据执行哪些操作。我有两个由制表符分隔的文本文件。

cat in1.tsv

111 A B C
111 D E F
111 G H I
222 A B C
333 A B C
333 D E F

这个表可以有大约数千行。列数少于 100。第一列可以有重复的值(如 111 和 333)。

cat in2.tsv

111 a b c 
222 a b c 
333 d e f

在此文件中,第 1 列中的值仅出现一次。我需要根据它的第一列匹配合并这两个文件。

cat output.tsv

111 A B C 111 a b c
111 D E F 111 a b c
111 G H I 111 a b c
222 A B C 222 a b c 
333 A B C 333 d e f
333 D E F 333 d e f 

如果矩阵的大小相同,我的解决方案有效:

paste  <(sort in1.tsv) <(sort in2.tsv) > output.tsv

感谢任何对 awk、bash 或其他适用于大量行的程序的帮助。

【问题讨论】:

  • 这看起来可以通过在 Python 或 R 中使用 pandas 模块来解决。如果您需要缩放,也许可以使用 Dask(另一个 Python 库)。
  • 感谢您的评论。我想先使用一些 awk 或 bash 解决方案,因为我对 python 不是很熟悉。

标签: python bash shell awk sed


【解决方案1】:

Awk 来救援!

awk 'BEGIN{FS=OFS="\t"}FNR==NR{for(i=2;i<=NF;i++) map[$1]=(map[$1] FS $i); next}$1 in map{print $0,$1,map[$1]}' in2.tsv in1.tsv

按照您的预期以制表符分隔格式生成输出。如果您不想分隔 o/p 制表符,请删除 OFS="\t"

就逻辑而言,创建一个映射,其中包含 in2.csv 上每列 1 的值到哈希映射 map[] 中,然后在 in1.csv 上选择那些包含 $1 的行,与形成的映射相同,打印行内容。

【讨论】:

  • 谢谢 Inian - 这是非常好的解决方案!
  • 我正在测试你的脚本,它可以工作,但它总是放空行 - 例如:111 A B C 111 EMPTY a b c.. 这是正常的吗?
【解决方案2】:

这是bash 方法:

首先让我们对每个文件进行排序:

LC_ALL=C sort init1.tsv -S75% -t$'\t' -k1,1 > init1.tsv.sorted

LC_ALL=C sort init2.tsv -S75% -t$'\t' -k1,1 > init2.tsv.sorted

然后,而不是pastingjoin 他们在第一列,

join init1.tsv.sorted init2.tsv.sorted -1 1 -2 2 -t$'\t'

如果您需要特定类型的连接,这似乎是左外连接,那么我会这样做:

join init1.tsv.sorted init2.tsv.sorted -1 1 -2 2 -t$'\t' -a1

快速说明,-S 指定您要使用多少 RAM,您希望此操作执行得越快,您应该使用的越多。

【讨论】:

  • 很好的解决方案,谢谢。您能解释一下排序的第一个操作 - LC_ALL=C 吗?
  • 它会覆盖所有本地设置,强制输出为默认语言。我也相信它会强制按位排序。
【解决方案3】:

join 命令似乎几乎可以满足您的要求:

$ join in1.tsv in2.tsv
111 A B C a b c
111 D E F a b c
111 G H I a b c
222 A B C a b c
333 A B C d e f
333 D E F d e f

默认行为是根据第一列以空格作为分隔符来连接行。使用格式选项-o 可以得到相同的结果。正如 Dmitry Polonskiy 在 cmets 中所说,也需要排序:

join -o 1.1,1.2,1.3,1.4,2.1,2.2,2.3,2.4 <(sort in1.tsv) <(sort in2.tsv)

【讨论】:

  • 您好,谢谢您的加入非常有用。如果数据未排序,是否需要对数据进行排序?
  • 是的,除非两个文件都先排序,否则加入将不起作用
【解决方案4】:

这可能对你有用(GNU sed):

 sed -r 's#^(\S+)\s.*#/^\1/s/$/ &/#' file2 | sed -f - file

从第二个文件创建一个 sed 脚本。此脚本由一个正则表达式组成,当匹配时将第二个文件中的匹配记录附加到第一个文件中的匹配记录。

【讨论】:

    【解决方案5】:

    在 Python 中,不依赖于被排序的文件:

    #!/usr/bin/env python
    
    with open("in1.tsv") as in1, open("in2.tsv") as in2:
        d = {line.split()[0]: line for line in in2}
        for line in in1:
            print(line.strip(), d[line.split()[0]], sep="\t", end="")
    

    这基本上创建了一个从第一列的值到in2.tsv 行的映射,然后循环in1.tsv 的行并使用映射将它们与in2.tsv 的相应行组合。

    【讨论】:

    • 感谢您的回复。可以像 python your_solution.py int1.tsv int2.tsv 那样运行这个表单终端吗?对不起,也许是愚蠢的问题。
    • 示例中的文件名是硬编码的。如果要传递参数,请改用sys.argv
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-08
    • 1970-01-01
    相关资源
    最近更新 更多