
Как свернуть категории KO с одинаковыми именами и вывести имена генов, которые были назначены каждой категории в массиве, как в примере ниже.
У меня есть это:
K00002 gene_65472
K00002 gene_212051
K00002 gene_403626
K00003 gene_666
K00003 gene_5168
K00003 gene_7635
K00003 gene_12687
K00003 gene_175295
K00003 gene_647659
K00003 gene_663019
K00004 gene_88381
K00005 gene_30485
K00005 gene_193699
K00005 gene_256294
K00005 gene_307497
И хочу это:
K00002 gene_65472 gene_212051 gene_403626
K00003 gene_666 gene_5168 gene_7635 gene_12687 gene_175295 gene_647659 gene_663019
K00004 gene_88381
K00005 gene_30485 gene_193699 gene_256294 gene_307497
Сработала следующая команда (взято изответ роаймы):
tr -d '\r' < file| awk '$1 != p { if (p>"") {printf "\n"} printf "%s",$1; p=$1 } { printf "\t%s",$2 } END { if(p>"") {printf "\n"} }' > output
решение1
Больше того же самого
awk '$1 != p { if (p>"") {printf "\n"} printf "%s",$1; p=$1 } { printf "\t%s",$2 } END { if(p>"") {printf "\n"} }' datafile
K00002 gene_65472 gene_212051 gene_403626
K00003 gene_666 gene_5168 gene_7635 gene_12687 gene_175295 gene_647659 gene_663019
K00004 gene_88381
K00005 gene_30485 gene_193699 gene_256294 gene_307497
Если вы не хотите разделениявкладказатем измените \t
на пробел.
Вот как это работает:
# Each line is processed in turn. "p" is the previous line's key field value
# Key field isn't the same as before
$1 != p {
# Flush this line if we have printed something already
if (p > "") { printf "\n" }
# Print the key field name and set it as the current key field
printf "%s", $1; p = $1
}
# Every line, print the second value on the line
{ printf "\t%s", $2 }
# No more input. Flush the line if we have already printed something
END {
if (p > "") { printf "\n" }
}
Изнечеткий КомментарииВыизготовлениеВопреки всем ответам, похоже, основная проблема в том, что вы используете файл данных, созданный в системе Windows, и ожидаете, что он будет работать на платформе UNIX/Linux. Не делайте этого. Или, если вам это необходимо, сначала преобразуйте файл в правильный формат.
dos2unix < datafile | awk '...' # As above
tr -d '\r' < data file | awk '...' # Also as above
решение2
файл:
K00002 gene_65472
K00002 gene_212051
K00002 gene_403626
K00003 gene_666
K00003 gene_5168
K00003 gene_7635
K00003 gene_12687
K00003 gene_654221
K00003 gene_663019
K00004 gene_88381
K00005 gene_30485
K00005 gene_193699
K00005 gene_256294
Использование awk:
awk '1 {if (a[$1]) {a[$1] = a[$1]" "$2} else {a[$1] = $2}} END {for (i in a) { print i,a[i]}}' file
выход:
K00002 gene_65472 gene_212051 gene_403626
K00003 gene_666 gene_5168 gene_7635 gene_12687 gene_654221 gene_663019
K00004 gene_88381
K00005 gene_30485 gene_193699 gene_256294
Я взял этопочтав качестве ссылки.
решение3
с использованием Миллераhttp://johnkerl.org/miller/doc
с
mlr --csv --implicit-csv-header --headerless-csv-output cat -n -g 1 then label a,b,c then reshape -s a,c then unsparsify --fill-with "" input.csv
и этот пример ввода csv
A,234
A,4945
B,8798
B,8798
B,790
У вас будет
A,234,4945,
B,8798,8798,790
решение4
Предположим, что ваши значения не содержат пробелов и разделены пробелами; также предположим, что ваши данные находятся в файле с именем file
(см. ниже версию с разделением табуляцией):
for x in $(<file cut -d ' ' -f 1 | sort | uniq); do
printf '%s %s\n' "$x" "$(grep "$x" file | cut -d ' ' -f 2- | tr '\n' ' ' | sed 's/.$//')"
done
Это будет:
- Извлеките отдельные значения первого поля:
cut
выбирает только первый фрагмент (-f 1
) строки, разбивая его на каждом пробеле (-d ' '
);sort | uniq
отсортирует значения первого поля и выведет каждое из них только один раз (в качестве альтернативы,короче и эффективнее:sort -u
);
- Для каждого:
- Извлеките все соответствующие строки из
file
с помощьюgrep
; - Удалим из них первое поле с помощью
cut
(-f 2-
означает «взять второе и последующие поля»); - Перевести остаток в список значений, разделенных пробелами (
tr
); - Избавьтесь от последнего символа — ненужного пробела — с помощью
sed
(да, это действительно неэлегантно); - Объедините результат со значением первого поля и выведите на стандартный вывод.
- Извлеките все соответствующие строки из
Если входные данные разделены табуляцией и вы хотите получить разделенный табуляцией вывод, приведенный выше код будет выглядеть следующим образом:
for x in $(<file cut -f 1 | sort | uniq); do
printf '%s\t%s\n' "$x" "$(grep "$x" file | cut -f 2- | tr '\n' '\t' | sed 's/.$//')"
done
Примечания:
- Производительность: время выполнения при таком подходе значительно выше, чем у
awk
базовых решений (я тестировалответ роаймы). По крайней мере, на порядок величины. - С другой стороны, этот подход работает даже если входной файл не упорядочен.
- Несмотря на то, что такое решение является быстрым (и грязным?) способом эффективного выполнения работы, обработка текста с помощью циклов оболочки, как правило, не рекомендуется; см. для справки "Почему использование цикла оболочки для обработки текста считается плохой практикой?".