2017-01-25 4 views
0

Я пытаюсь использовать unix для преобразования файла с разделителями табуляции из формата короткого/широкого формата в длинный, аналогично функции reshape в R. Надеюсь создать три строки для каждого строка в стартовом файле. В столбце 4 в настоящее время содержится 3 значения, разделенные запятыми. Я надеюсь, что колонки 1, 2 и 3 будут одинаковыми для каждой стартовой строки, но столбец 4 будет одним из значений из начального столбца 4. Этот пример, вероятно, делает его более понятным, чем я могу описать в устной форме:Переформатирование из широкоформатного формата

current file: 
A1 A2 A3 A4,A5,A6 
B1 B2 B3 B4,B5,B6 
C1 C2 C3 C4,C5,C6 

goal: 
A1 A2 A3 A4 
A1 A2 A3 A5 
A1 A2 A3 A6 
B1 B2 B3 B4 
B1 B2 B3 B5 
B1 B2 B3 B6 
C1 C2 C3 C4 
C1 C2 C3 C5 
C1 C2 C3 C6 

как кто-то просто Ознакомившись с этим языком, моя первая мысль была использовать СЭД, чтобы найти запятые заменить жесткий возвращения

sed 's/,/&\n/' data.frame

Я действительно не знаю, как включить значения для столбцов 1 -3. У меня были низкие надежды на эту работу, но единственное, что я мог подумать, - попробовать вставить значения столбца с помощью {print $ 1, $ 2, $ 3}.

sed 's/,/&\n{print $1, $2, $3}/' data.frame

Не к моему удивлению, выход выглядел следующим образом:

A1 A2 A3 A4 
{print $1, $2, $3} A5 
{print $1, $2, $3} A6 
B1 B2 B3 B4 
{print $1, $2, $3} B5 
{print $1, $2, $3} B6 
C1 C2 C3 C4 
{print $1, $2, $3} C5 
{print $1, $2, $3} C6 

Похоже, этот подход может быть для хранения значений столбцов 1-3, а затем вставить их. Я не уверен, как хранить значения, я думаю, что это может быть связано с адаптацией следующего сценария, но мне трудно понять все компоненты.

NR==FNR{a[$1, $2, $3]=1}

Заранее спасибо за ваши мысли по этому вопросу.

ответ

1

Вы можете от записи простой read петли для этого и использовать расширение скобок для разбора через запятую поля:

#!/bin/bash 

while read -r f1 f2 f3 c1; do 
    # split the comma delimited field 'c1' into its constituents 
    for c in ${c1//,/ }; do 
    printf "$f1 $f2 $f3 $c\n" 
    done 
done < input.txt 

Выход:

A1 A2 A3 A4 
A1 A2 A3 A5 
A1 A2 A3 A6 
B1 B2 B3 B4 
B1 B2 B3 B5 
B1 B2 B3 B6 
C1 C2 C3 C4 
C1 C2 C3 C5 
C1 C2 C3 C6 
+0

Большое вам спасибо, это сработало красиво! – user4670961

+1

Зачем использовать подседельную систему sed? Достаточно ли '$ {c1 //, /}'? – ghoti

+0

Отличный пункт @ghoti. Я изменил код. – codeforester

1

В растворе без вызова внешней программы:

#!/bin/bash 

data_file="d" 

while IFS=" " read -r f1 f2 f3 r 
do 
    IFS="," read f4 f5 f6 <<<"$r" 
    printf "$f1 $f2 $f3 $f4\n$f1 $f2 $f3 $f5\n$f1 $f2 $f3 $f6\n" 
done <"$data_file" 
0

Если вам не нужен выходной сигнал в какой-либо форме лар порядок в группе четвертой колонки, следующий AWK один вкладыш может сделать:

awk '{split($4,a,","); for(i in a) print $1,$2,$3,a[i]}' input.txt 

Это работает, разделив свой 4-й столбец в массив, то для каждого элемента массива, печатая «новый» четыре столбца.

Если важен порядок - то есть, A4 должна предшествовать A5, и т.д., то вы можете использовать классический for цикл:

awk '{split($4,a,","); for(i=1;i<=length(a);i++) print $1,$2,$3,a[i]}' input.txt 

Но это AWK. И вы спрашиваете о bash.

Следующая может работать:

#!/usr/bin/env bash 

mapfile -t arr < input.txt 

for s in "${arr[@]}"; do 
    t=($s) 
    mapfile -t -d, u <<<"${t[3]}" 
    for v in "${u[@]}"; do 
    printf '%s %s %s %s\n' "${t[@]:0:3}" "${v%$'\n'}" 
    done 
done 

Это копирует свой весь входной файл в элементы массива, а затем шаги через этот массив, отображение каждого 4-го столбца во второй массив.Затем он проходит через этот второй массив, печатая первые три столбца из первого массива вместе с текущим полем из второго массива.

Это, по-видимому, аналогично по своей структуре альтернативе awk, но гораздо более громоздко читать и кодировать.

Обратите внимание на ${v%$'\n'} на линии printf. Это удаляет конечную новую строку последнего поля, которая не разделяется mapfile, потому что мы используем альтернативный разделитель.

Обратите внимание, что нет причин, по которым вы имеете, чтобы скопировать весь ваш вход в массив, я просто сделал это, чтобы продемонстрировать немного больше mapfile. Разумеется, вы можете использовать старый стандарт,

while read s; do 
    ... 
done < input.txt 

если вы предпочитаете.

+0

Несколько вопросов о 'mapfile', поскольку я не использовал его раньше и мне интересно. Как правило, при чтении файла, если вам нужно разбить его по строкам, это означает, что вы, вероятно, собираетесь перебирать эти строки и, таким образом, в любом случае будете иметь цикл while. Может быть, он работает лучше? Но тогда, чтобы извлечь из этого выгоду, вам понадобится файл, достаточно большой, чтобы загрузить его в память может стать проблемой. Если вам нужно сохранить внутреннюю часть цикла в области основной оболочки, вы можете использовать '<()' вместо канала. Существуют ли реальные сценарии, в которых «mapfile» будет казаться естественным? – Fred

+0

@Fred, вы правы, чтобы задать вопрос. Этот материал всегда трудно оценить. Для меня это обычно сводится к тому, что даже если разница в производительности * обнаруживается *, я не буду считать ее фактором, если это не заметно *. Конечно, материал можно измерить. Но при прочих равных условиях, если это не влияет на ваш день, то идите с тем, что чувствует себя правильно или быстрее программируете, потому что на самом деле не имеет значения, какой метод вы выбираете. – ghoti

+0

Я понимаю, что вы говорите. Мой вопрос заключался в том, чтобы больше узнать, в каких случаях (в режиме чтения) вы, как правило, используете 'mapfile' вместо' read' с циклом или awk-скриптом. Я пытаюсь выяснить, когда «mapfile» станет вашим «лучшим инженерным компромиссом». – Fred

Смежные вопросы