2017-01-03 4 views
4

У меня есть файл со следующим данных-gsub много столбцов одновременно на основе разных условий gsub?

INPUT-

A B C D E F 
A B B B B B 
C A C D E F 
A B D E F A 
A A A A A F 
A B C B B B 

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

Желаемая output-

1 1 1 1 1 1 
1 1 B B B B 
C A 1 1 1 1 
1 1 D E F A 
1 A A A A 1 
1 1 1 B B B 

Первая строка становится все 1, так как оно идентично себе (очевидно). Во втором ряду первый и второй столбцы идентичны первой строке (A B) и, следовательно, они становятся 1 1. И так далее для других строк.

Я написал следующий код, который делает это transformation-

for seq in {1..1} ; #Iterate over the rows (in this case just row 1) 
do 
    for position in {1..6} ; #Iterate over the columns 
    do 
     #Define the letter in the first row with which I'm comparing the rest of the rows 
     aa=$(awk -v pos=$position -v line=$seq 'NR == line {print $pos}' f) 
     #If it matches, gsub it to 1 
     awk -v var=$aa -v pos=$position '{gsub (var, "1", $pos)} 1' f > temp 
     #Save this intermediate file and now act on this 
     mv temp f 
    done 
done 

Как вы можете себе представить, что это очень медленно, потому что вложенный цикл дорого. Мои реальные данные - это матрица 60x10000, и для этой программы требуется около 2 часов.

Я надеялся, что вы сможете помочь мне избавиться от внутреннего цикла, чтобы я мог делать все 6 gsubs за один шаг. Может быть, это их собственный массив? Мои навыки awk еще невелики.

+1

Пожалуйста, обратите внимание на: [Что я должен делать, когда кто-то ответит на мой вопрос?] (Http://stackoverflow.com/help/someone-answers) – Cyrus

ответ

3

Входной

$ cat f 
A B C D E F 
A B B B B B 
C A C D E F 
A B D E F A 
A A A A A F 
A B C B B B 

Желаемая о/р

$ awk 'FNR==1{split($0,a)}{for(i=1;i<=NF;i++)if (a[i]==$i) $i=1}1' f 
1 1 1 1 1 1 
1 1 B B B B 
C A 1 1 1 1 
1 1 D E F A 
1 A A A A 1 
1 1 1 B B B 

Объяснение

  • FNR==1{ .. }

Когда awk читает первую запись текущего файла, делать вещи в фигурных скобках

раскола (строка, массив [, fieldsep [, SEPS]])

Разделить строку на часть, разделенное fieldsep и храните куски в массиве и разделительные строки в массиве сепсов.

  • split($0,a)

сплит записи текущего или строки ($0) на куски fieldsep (Defualt пространство, так как мы не поставляется 3-й аргумент) и хранить части в массиве a Таким образом, массив a содержит данные из первого ряда

 a[1] = A 
     a[2] = B 
     a[3] = C 
     a[4] = D 
     a[5] = E 
     a[6] = F 
  • for(i=1;i<=NF;i++)

Перебор всех полей для каждой записи файла до конца файла.

  • if (a[i]==$i) $i=1

, если значение первой строки в столбце текущего индекса (i) равно текущего значения столбца текущей строки установить текущее значение столбца = 1 (то есть изменить текущее значение столбца)

Теперь мы изменили значение столбца рядом только с измененной строки строки

  • }1

    1 всегда истинно, она выполняет операцию {print $0}

по умолчанию для запроса обновления на комментарий

Тот же вопрос здесь, у меня есть вторая часть программы, которая добавляет цифры в строках. То есть Вы получите 6, 2, 4, 2, 2, 3 для этого вывода . Может ли ваша программа быть настроена, чтобы получить эти значения на этом этапе ?

$ awk 'FNR==1{split($0,a)}{s=0;for(i=1;i<=NF;i++)if(a[i]==$i)s+=$i=1;print $0,s}' f 
1 1 1 1 1 1 6 
1 1 B B B B 2 
C A 1 1 1 1 4 
1 1 D E F A 2 
1 A A A A 1 2 
1 1 1 B B B 3 
+0

Это работает чудесно и так же быстро, как и решение @anubhava. +1. – VM17

+0

@VarunM Рад узнать. –

+0

Тот же вопрос, у меня есть вторая часть программы, которая добавляет числа в строках. То есть Вы получите 6, 2, 4, 2, 2, 3 для этого выхода.Можно ли настроить вашу программу, чтобы получить эти значения на этом этапе? – VM17

4

Вы можете использовать эту простую команду AWK, чтобы сделать работу, которая будет быстрее закончить, как мы избегаем вложенных циклов в оболочке, а также применение AWK несколько раз в вложенном цикле:

awk '{for (i=1; i<=NF; i++) {if (NR==1) a[i]=$i; if (a[i]==$i) $i=1} } 1' file 

1 1 1 1 1 1 
1 1 B B B B 
C A 1 1 1 1 
1 1 D E F A 
1 A A A A 1 
1 1 1 B B B 

EDIT :

согласно комментариям ниже здесь является то, что вы можете сделать, чтобы получить сумму каждого столбца в каждой строке:

awk '{sum=0; for (i=1; i<=NF; i++) { if (NR==1) a[i]=$i; if (a[i]==$i) $i=1; sum+=$i} 
     print $0, sum}' file 

1 1 1 1 1 1 6 
1 1 B B B B 2 
C A 1 1 1 1 4 
1 1 D E F A 2 
1 A A A A 1 2 
1 1 1 B B B 3 
+1

'' ++ для простоты! – Inian

+1

, и я уверен, что это быстрее :) – Cyrus

+0

Это работает как шарм. Сделал мою общую программу примерно в 3,5 раза быстрее. У меня есть вторая часть программы, которая суммирует числа в строках. То есть Вы получите 6, 2, 4, 2, 2, 3 для этого выхода. Можно ли настроить вашу программу, чтобы получить эти значения на этом этапе? Должен ли я задать это как отдельный вопрос? – VM17

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