Подмножество файлов по номерам строк и столбцов
Мы хотим подмножить текстовый файл в строках и столбцах, где строки и столбцы считываются из файла. Исключая заголовок (строка 1) и имена ростов (col 1).
inputFile.txt Текстовый файл с разделителями табуляции
header 62 9 3 54 6 1
25 1 2 3 4 5 6
96 1 1 1 1 0 1
72 3 3 3 3 3 3
18 0 1 0 1 1 0
82 1 0 0 0 0 1
77 1 0 1 0 1 1
15 7 7 7 7 7 7
82 0 0 1 1 1 0
37 0 1 0 0 1 0
18 0 1 0 0 1 0
53 0 0 1 0 0 0
57 1 1 1 1 1 1
subsetCols.txt Запятая разделена без пробелов, одной строки, упорядоченных чисел. В реальных данных у нас есть 500K столбцов и нужно подмножество ~ 10K.
1,4,6
subsetRows.txt Запятая разделена без пробелов, одной строки, упорядоченных чисел. В реальных данных у нас есть 20K строк и нужно подмножество около ~ 300.
1,3,7
Текущее решение с использованием цикла cut и awk (Связанное сообщение: выберите строки с помощью awk):
# define vars
fileInput=inputFile.txt
fileRows=subsetRows.txt
fileCols=subsetCols.txt
fileOutput=result.txt
# cut columns and awk rows
cut -f2- $fileInput | cut -f`cat $fileCols` | sed '1d' | awk -v s=`cat $fileRows` 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' > $fileOutput
Выходной файл: result.txt
1 4 6
3 3 3
7 7 7
Вопрос:
Это решение отлично подходит для небольших файлов, для больших файлов 50K строк и 200K столбцов, занимает слишком много времени, 15 минут плюс, все еще работает. Я думаю, что вырезать ting столбцы отлично работают, выбор строк - медленный бит.
Лучший способ?
Информация о реальных входных файлах:
# $fileInput:
# Rows = 20127
# Cols = 533633
# Size = 31 GB
# $fileCols: 12000 comma separated col numbers
# $fileRows: 300 comma separated row numbers
Дополнительная информация о файле: файл содержит данные генотипа GWAS. Каждая строка представляет образец (индивидуальный), и каждый столбец представляет SNP. Для дальнейшего анализа на основе регионов нам нужно подмножество выборок (строк) и SNP (столбцов), чтобы сделать данные более управляемыми (малыми) в качестве ввода для других статистических программ, таких как r.
Система:
$ uname -a
Linux nYYY-XXXX ZZZ Tue Dec 18 17:22:54 CST 2012 x86_64 x86_64 x86_64 GNU/Linux
Обновление: Решение, приведенное ниже @JamesBrown, смешивало заказы столбцов в моей системе, так как я использую другую версию awk, моя версия: GNU Awk 3.1.7
Ответы
Ответ 1
Даже если в Если языки программирования были странами, в какой стране будет отображаться каждый язык? они говорят, что...
Awk: Северная Корея. Упрямо сопротивляется изменениям, и его пользователи, похоже, неестественно любят его по причинам, о которых мы можем только догадываться.
... всякий раз, когда вы видите, что вы трахаете sed, cut, grep, awk и т.д., останавливайтесь и говорите себе: awk может сделать это в одиночку!
Итак, в этом случае речь идет об извлечении строк и столбцов (настройка их для исключения заголовка и первого столбца), а затем просто буферизация вывода для окончательной печати.
awk -v cols="1 4 6" -v rows="1 3 7" '
BEGIN{
split(cols,c); for (i in c) col[c[i]] # extract cols to print
split(rows,r); for (i in r) row[r[i]] # extract rows to print
}
(NR-1 in row){
for (i=2;i<=NF;i++)
(i-1) in col && line=(line ? line OFS $i : $i); # pick columns
print line; line="" # print them
}' file
С вашим примером файла:
$ awk -v cols="1 4 6" -v rows="1 3 7" 'BEGIN{split(cols,c); for (i in c) col[c[i]]; split(rows,r); for (i in r) row[r[i]]} (NR-1 in row){for (i=2;i<=NF;i++) (i-1) in col && line=(line ? line OFS $i : $i); print line; line=""}' file
1 4 6
3 3 3
7 7 7
С вашим примером файла и вводами в качестве переменных разделите запятую:
awk -v cols="$(<$fileCols)" -v rows="$(<$fileRows)" 'BEGIN{split(cols,c, /,/); for (i in c) col[c[i]]; split(rows,r, /,/); for (i in r) row[r[i]]} (NR-1 in row){for (i=2;i<=NF;i++) (i-1) in col && line=(line ? line OFS $i : $i); print line; line=""}' $fileInput
Я уверен, что это будет быстрее. Вы можете, например, проверить Удалить дубликаты из текстового файла на основе второго текстового файла для некоторых тестов, сравнивающих производительность awk
над grep
и другими.
Best,
Ким Чен-ун
Ответ 2
Один в Gnu awk версии 4.0 или новее, поскольку упорядочение столбцов зависит от for
и PROCINFO["sorted_in"]
. Номера строк и столбцов считываются из файлов:
$ awk '
BEGIN {
PROCINFO["sorted_in"]="@ind_num_asc";
}
FILENAME==ARGV[1] { # process rows file
n=split($0,t,",");
for(i=1;i<=n;i++) r[t[i]]
}
FILENAME==ARGV[2] { # process cols file
m=split($0,t,",");
for(i=1;i<=m;i++) c[t[i]]
}
FILENAME==ARGV[3] && ((FNR-1) in r) { # process data file
for(i in c)
printf "%s%s", $(i+1), (++j%m?OFS:ORS)
}' subsetRows.txt subsetCols.txt inputFile.txt
1 4 6
3 3 3
7 7 7
Вероятно, некоторое увеличение производительности может быть связано с перемещением блока обработки ARGV[3]
в верхние ячейки 1 и 2 и добавлением к нему конца next
.
Ответ 3
Не отнимать ничего от превосходных ответов. Просто потому, что эта проблема связана с большим набором данных, я отправляю комбинацию из 2 ответов для ускорения обработки.
awk -v cols="$(<subsetCols.txt)" -v rows="$(<subsetRows.txt)" '
BEGIN {
n = split(cols, c, /,/)
split(rows, r, /,/)
for (i in r)
row[r[i]]
}
(NR-1) in row {
for (i=1; i<=n; i++)
printf "%s%s", $(c[i]+1), (i<n?OFS:ORS)
}' inputFile.txt
PS: Это должно работать со старой awk-версией или без gnu awk.
Ответ 4
для уточнения решения @anubhava мы можем
избавиться от поиска более 10 тыс. значений для каждой строки
чтобы увидеть, находимся ли мы в правильной строке, воспользовавшись тем, что вход уже отсортирован.
awk -v cols="$(<subsetCols.txt)" -v rows="$(<subsetRows.txt)" '
BEGIN {
n = split(cols, c, /,/)
split(rows, r, /,/)
j=1;
}
(NR-1) == r[j] {
j++
for (i=1; i<=n; i++)
printf "%s%s", $(c[i]+1), (i<n?OFS:ORS)
}' inputFile.txt
Ответ 5
Python имеет модуль csv. Вы читаете строку в списке, печатаете нужные столбцы в stdout, ополаскиваете, стираете, повторяете.
Это должно срезать столбцы от 20 000 до 30 000.
import csv
with open('foo.txt') as f:
gwas = csv.reader(f, delimiter=',', quoting=csv.QUOTE_NONE)
for row in gwas:
print(row[20001:30001]