Подтвердить что ты не робот

Подмножество файлов по номерам строк и столбцов

Мы хотим подмножить текстовый файл в строках и столбцах, где строки и столбцы считываются из файла. Исключая заголовок (строка 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

4b9b3361

Ответ 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]