【问题标题】:How to parse data to coresponding columns in awk如何将数据解析到awk中的相应列
【发布时间】:2020-05-01 05:35:22
【问题描述】:

我有 TAB 单独的数据,我想像这样解析数据:

输入:

more input.tsv

A  B  5  A1,A2,A3,A4,A5   B1,B2,B3,B4,B5
C  D  3  C1,C2,C3  D1,D2,D3

所需的输出是:

A  B  5  A1  B1
A  B  5  A2  B2
.
.
A  B  5  A5  B5
C  D  3  C1  D1
.
C  D  3  C3  D3

所以这意味着保留前三列并将第 4 列和第 5 列拆分为相应的值。第 4 列和第 5 列中的值的数量是第 3 列中的定义值。

我更喜欢 awk 或带有示例解释的 python - 以便于理解和学习一些东西。

我的尝试没有任何循环:

awk '{OFS="\t"}{split($4,arr4,",") split($5,arr5,","); print $1,$2,$3,arr4[1],arr5[1]; print $1,$2,$3,arr4[2],arr5[2]}'

【问题讨论】:

  • 您实际上并不是在问问题。通过描述您当前方法存在的问题,甚至没有隐含地。请阅读How to Ask

标签: python shell parsing awk


【解决方案1】:

在 Python 中你可以这样做:

tempstr = """A\tB\t5\tA1,A2,A3,A4,A5\tB1,B2,B3,B4,B5
C\tD\t3\tC1,C2,C3\tD1,D2,D3"""

data = []

for line in tempstr.split("\n"):
    line = line.split("\t")
    split_column_1 = line[3].split(",")
    split_column_2 = line[4].split(",")
    if len(split_column_1) != len(split_column_2):
        print("Something wrong")
    else:
        for c1,c2 in zip(split_column_1,split_column_2):
            data.append((line[0],line[1],line[2],c1,c2))

for d in data:
    print("\t".join(d))

输出:

A   B   5   A1  B1
A   B   5   A2  B2
A   B   5   A3  B3
A   B   5   A4  B4
A   B   5   A5  B5
C   D   3   C1  D1
C   D   3   C2  D2
C   D   3   C3  D3

有TSV文件

您可以使用 csv 模块来处理您的数据:

import csv

data = []

with open('resources/data.tsv') as csv_file:
    csv_reader = csv.reader(csv_file, delimiter='\t')
    for row in csv_reader:
        split_column_1 = row[3].split(",")
        split_column_2 = row[4].split(",")
        if len(split_column_1) != len(split_column_2):
            print("Something wrong")
        else:
            for c1, c2 in zip(split_column_1, split_column_2):
                data.append((row[0], row[1], row[2], c1, c2))

for d in data:
    print("\t".join(d))

说明

  1. 使用 csv 模块打开文件。优点是它已经对我们指定的分隔符进行了拆分。默认应该是 "," 但我们使用的是 \t,因为我们有一个 tsv 文件。
with open('resources/data.tsv') as csv_file:
    csv_reader = csv.reader(csv_file, delimiter='\t')
  1. 我们遍历每一行/每一行。 csv 模块的一个功能也可以通过 for 循环轻松完成。

for row in csv_reader:

  1. 现在我们将第四列和第五列拆分为“,”,因为它们仍然是字符串。现在我们有了一个包含拆分元素的列表。
split_column_1 = row[3].split(",")
split_column_2 = row[4].split(",")
  1. 如果这两个的长度不同,则数据有问题,可能导致意外事件。 (取决于您的代码)因此我们会检查是否是这种情况(如果您的数据没有任何错误,则永远不会是真的)
if len(split_column_1) != len(split_column_2):
    print("Something wrong")
  1. 我们将所有数据保存为列表中的元组。如果需要,您稍后还可以在稍后的步骤中访问此数据(例如 data[3][3] # 4th row, 4th element -> A4
else:
    for c1, c2 in zip(split_column_1, split_column_2):
        data.append((row[0], row[1], row[2], c1, c2))
  1. 打印得很好,使它看起来像您预期的输出。基本上,您可以在字符串上使用连接(在我们的例子中,我们采用\t)并使用元组/列表作为参数。现在他将元组/列表的所有元素与左侧的字符串连接起来:
for d in data:
    print("\t".join(d))

【讨论】:

  • 你可以在迭代时使用 zip 函数(比在长度范围内迭代更 Pythonic)。 for element1, element2 in zip(split_column_1, split_column_2):
  • 谢谢!如果我的文件保存为 input.tsv,如何将其加载到脚本中?刚刚在我的问题中编辑。
  • @Boendal 非常感谢你,这太棒了 - 像魅力一样工作。你能解释一下第二个循环吗?
  • @Paul 看到我的编辑......也许我应该更仔细地阅读我解释了一切......无论如何,如果你有进一步的问题,请告诉我。
  • @Boendal 这绝对是惊人的解释!非常感谢您提供清晰而好的解决方案!
【解决方案2】:

带有 sed 循环的好正则表达式:

# recreate input
# tr to replace spaces with tabs, as the input is tsv
tr -s ' ' '\t' <<EOF |
A  B  5  A1,A2,A3,A4,A5   B1,B2,B3,B4,B5
C  D  3  C1,C2,C3  D1,D2,D3
EOF
# sed script
sed -E '
   # label a
   : a
   # take the last items after `,` comma
   # and add a new line to the pattern space with the two items
   # and remove the last items from the list in the first line
   s/([^\t]+\t[^\t]+\t[^\t]+\t)(.+),([^\t]+)\t(.+),([^\n]+)/\1\2\t\4\n\1\3\t\5/
   # if the last substitution was successfull, branch to label a
   t a
'

on repl gives the following output:

A   B   5   A1  B1
A   B   5   A2  B2
A   B   5   A3  B3
A   B   5   A4  B4
A   B   5   A5  B5
C   D   3   C1  D1
C   D   3   C2  D2
C   D   3   C3  D3

还有一个没有扩展正则表达式的单行:

sed ':a;s/\([^\t]*\t[^\t]*\t[^\t]*\t\)\(.*\),\([^\t]*\)\t\(.*\),\([^\n]*\)/\1\2\t\4\n\1\3\t\5/;ta'

【讨论】:

    【解决方案3】:

    你能不能尝试一下,目前还没有测试过。

    awk '
    BEGIN{
      FS=OFS="\t"
    }
    {
      num1=split($4,array1,",")
      num2=split($5,array2,",")
      till=num1>num2?num1:num2
      for(j=1;j<=till;j++){
        print $1,$2,$3,array1[j],array2[j]
      }
      delete array1
      delete array2
    }
    '  Input_file
    

    在不将字段分隔符设置为 TAB 的情况下测试上述代码:

    awk '
    {
      num1=split($4,array1,",")
      num2=split($5,array2,",")
      till=num1>num2?num1:num2
      for(j=1;j<=till;j++){
        print $1,$2,$3,array1[j],array2[j]
      }
      delete array1
      delete array2
    }
    ' Input_file
    
    A B 5 A1 B1
    A B 5 A2 B2
    A B 5 A3 B3
    A B 5 A4 B4
    A B 5 A5 B5
    C D 3 C1 D1
    C D 3 C2 D2
    C D 3 C3 D3
    

    【讨论】:

    • 感谢您的回复。输出为空。
    • @Paul,如果您的 Input_file 不是制表符分隔的,那么您可以尝试删除 BEGIN 部​​分的 FS、OFS 值并尝试一下?
    • 输入文件以制表符分隔。好消息是,没有错误。坏消息是输出仍然是空的。
    • @Paul,我已经成功测试了我的代码,并且看起来您的文件没有 TAB 分隔符,当我尝试删除此代码的 BEGIN 部​​分时,它对我有用,请查看我在哪里添加了哪个输出我得到了,干杯。
    猜你喜欢
    • 2018-04-13
    • 2017-04-07
    • 1970-01-01
    • 2020-12-26
    • 2012-03-21
    • 2017-01-24
    • 1970-01-01
    • 1970-01-01
    • 2015-01-16
    相关资源
    最近更新 更多