Как проанализировать данные в соответствующие столбцы в awk - PullRequest
0 голосов
/ 14 января 2020

У меня есть отдельные данные 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 с пояснением примера - чтобы легко понять и выучить что-то.

Моя попытка без любой л oop:

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]}'

Ответы [ 3 ]

1 голос
/ 14 января 2020

В 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')
Мы go через каждую строку / строку. Также особенность модуля csv - сделать это легко с помощью для l oop.

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")
Мы сохраняем все данные в виде кортежа в списке. Позже вы также можете получить доступ к этим данным на более позднем этапе, если вам нужно (например, 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))
Распечатайте его так, чтобы он выглядел как ожидаемый результат. По сути, вы можете использовать соединение со строкой (в нашем случае мы берем \t) и в качестве параметра использовать кортеж / список. Теперь он объединяет все элементы кортежа / списка со строкой слева:
for d in data:
    print("\t".join(d))
1 голос
/ 14 января 2020

Хорошее регулярное выражение с sed l oop:

# 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
'

в ответе дает следующий вывод :

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'
1 голос
/ 14 января 2020

Не могли бы вы попробовать следующее, пока не проверялось.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...