Я знаю, что есть несколько сообщений для объединения нескольких файлов, но это заняло так много времени. У меня есть несколько файлов, в которых первые столбцы предназначены для идентификаторов пациентов, затем я хочу объединить несколько файлов на основе идентификационных номеров в первом столбце.

Коды, как показано ниже, все еще работают, но это заняло так много времени. Таким образом, кто-нибудь знает более эффективный способ выполнения этого процесса?

for PHENO in A B C D E F G H I J K L M
do
    join -a1 -a2 -e 1 -o auto chr2_${PHENO} chr3_${PHENO} >${PHENO}
done

for PHENO in A B C D E F G H I J K L M
do
    for file in chr5_${PHENO} chr11_${PHENO} chr14_${PHENO} chr20_${PHENO} \
    chr21_${PHENO} chr22_${PHENO} chr6_${PHENO} chr9_${PHENO} chr13_${PHENO} \
    chr18-1_${PHENO} chr18-2_${PHENO} chr1-1_${PHENO} chr1-2_${PHENO} \
    chr1-3_${PHENO} chr8-1_${PHENO} chr8-2_${PHENO} chr17-1_${PHENO} \
    chr17-2_${PHENO} chr19-1_${PHENO} chr19-2_${PHENO} chr19-3_${PHENO} \
    chr19-4_${PHENO} chr4-1_${PHENO} chr4-2_${PHENO} chr4-3_${PHENO} \
    chr4-4_${PHENO} chr7-1_${PHENO} chr7-2_${PHENO} chr7-3_${PHENO} \
    chr10-1_${PHENO} chr10-2_${PHENO} chr10-3_${PHENO} chr10-4_${PHENO} \
    chr12-1_${PHENO} chr12-2_${PHENO} chr12-3_${PHENO} chr12-4_${PHENO} \
    chr15-1_${PHENO} chr15-2_${PHENO} chr15-3_${PHENO} chr16-1_${PHENO} \
    chr16-2_${PHENO} chr16-3_${PHENO}; do
        join -a1 -a2 -e 1 -o auto ${PHENO} "$file" >${PHENO}.1
        mv ${PHENO}.1 ${PHENO}
    done
done

Все файлы, как показано ниже. 150001 пациент, показывающий наличие у них заболевания в виде 0 или 1. Например, chr2_${PHENO}

ID Disease
1 0
2 1
3 0 
4 1
5 1
....

150000 0 
150001 1

Например, chr3_${PHENO}

ID Disease
1 1
2 1
3 1 
4 0
5 0
....

150000 0 
150001 0

Заранее спасибо!

0
ibuprofen 8 Июн 2021 в 07:52
2
Можно узнать размеры файлов? Кажется, есть примерно 50 файлов, к которым вы хотите присоединиться, все 50 поместятся в оперативной памяти одновременно? В вашем коде есть "-e 1", это действительно используется? У вас также есть "-o auto", это нужно? Большим плюсом соединения является то, что оно может обрабатывать миллиарды строк даже на небольших машинах, поскольку, по сути, ему нужно хранить только одну строку из каждого файла в памяти за раз.
 – 
icarus
8 Июн 2021 в 04:34
Спасибо за ваш комментарий. Я добавил примеры каждого файла.
 – 
user10345633
8 Июн 2021 в 04:43
2
Файлы примеров не сортируются... "Важно: ФАЙЛ1 и ФАЙЛ2 должны быть отсортированы по полям соединения." - и вы не сортируете при объединении. Чего здесь не хватает? И: каждый файл имеет одинаковый диапазон идентификаторов (без дыр)?
 – 
ibuprofen
8 Июн 2021 в 06:49
Спасибо. Если отсортировано, не нужно добавлять -a1 и -a2?
 – 
user10345633
8 Июн 2021 в 07:08
1
Это зависит от того, что вы хотите получить в итоге. Если вы не укажете -a1 и -a2, то любой идентификатор, отсутствующий в ФАЙЛЕ1, который находится в ФАЙЛЕ2, не будет напечатан, и наоборот. Скажем, ID 5 находится в ФАЙЛЕ1, но нет в ФАЙЛЕ2. Тогда пропуск -a1 исключит всю строку с ID 5. Нужно ли это, зависит от двух вещей. Если вы хотите, чтобы это было опущено, и если файлы имеют дыры? Например, может ли файл1 иметь идентификатор 1, 2, 4, 5 и идентификатор файла2 2, 3, 4, 5? В файле 1 отсутствует идентификатор 3, а в файле 2 отсутствует идентификатор 1?
 – 
ibuprofen
8 Июн 2021 в 07:31

3 ответа

Лучший ответ

В ПОРЯДКЕ. Это не ответ как таковой, а сообщение, возможно, разъясняющее ситуацию.

Включите то, что вам нужно в ваш вопрос.

(И извините, знайте, что это не так, как обычно, но...)


Это похоже на ваши файлы и требуемый результат?

Вот два примера скриптов. Первый для создания фиктивных файлов:

  • От chr1_A до chr6_A
  • От chr1_B до chr6_B
  • От chr1_C до chr6_C

Отсортировано с использованием LC_ALL=C sort -k 1b,1

#! /bin/bash
for p in A B C; do
    for i in $(seq 1 6); do
        f="chr${i}_$p"
        printf 'ID %s\n' "$i.$p" >"$f"
        paste <(shuf -n 24 -i 1-222) <(shuf -n 24 -i 0-1 -r) | \
            LC_ALL=C sort -k 1b,1 >>"$f"
    done
done

Предоставление группы образцов, например: paste chr* | column -t

ID   1.A  ID   1.B  ID   1.C  ID   2.A  ID   2.B  ID   2.C  ...
116  1    107  1    101  0    110  1    105  1    111  0    ...
126  1    11   1    105  0    111  1    106  1    117  1    ...
131  1    111  0    106  0    121  0    113  0    121  0    ...
141  0    133  0    110  0    124  0    147  0    145  0    ...
167  1    135  1    113  1    135  0    154  0    146  1    ...
...

Не уверен, что это правильно, решать вам.


Второй сценарий является модифицированной версией вашего (например, с использованием тире для отсутствия, чтобы его можно было отличить от реальных данных):

#! /bin/bash

for PHENO in A B C; do
    join -a1 -a2 -e - -o auto chr1_${PHENO} chr2_${PHENO} >${PHENO}
done

for PHENO in A B C; do
    for n in 3 4 5 6; do
        file="chr${n}_$PHENO"
        join -a1 -a2 -e - -o auto ${PHENO} "$file" >${PHENO}.1
        mv ${PHENO}.1 ${PHENO}
    done
done

В результате получилось три файла A, B и C:

$ paste A B C | column -t
ID   1.A  2.A  3.A  4.A  5.A  6.A  ID   1.B  2.B  3.B  4.B  5.B  6.B  ID   1.C  2.C  3.C  4.C  5.C  6.C
10   -    -    1    1    -    -    101  -    -    1    -    -    1    101  0    -    0    -    -    1
100  -    -    -    0    -    -    102  -    -    -    -    -    0    103  -    -    -    -    -    0
102  -    -    1    -    0    -    105  -    1    0    -    0    -    105  0    -    -    -    -    -
108  -    -    0    -    -    -    106  -    1    -    -    -    1    106  0    -    -    -    1    -
109  -    -    -    -    -    1    107  1    -    -    -    -    -    107  -    -    -    -    -    0
110  -    1    -    -    -    -    109  -    -    -    -    -    0    108  -    -    -    -    -    0
111  -    1    -    -    -    -    11   1    -    -    -    -    -    109  -    -    -    1    0    -
116  1    -    -    -    -    -    111  0    -    -    -    -    -    110  0    -    -    -    -    -
117  -    -    -    -    1    -    113  -    0    -    -    -    -    111  -    0    -    -    -    -

...

# or
# paste <(sort -n A) <(sort -n B) <(sort -n C) | column -t

1
ibuprofen 8 Июн 2021 в 10:31

Я бы предложил другой подход:

  1. Убедитесь, что все файлы, такие как chr1_A, имеют полные 15000 записей, отсортированных! Это также включает в себя заполнение «1» там, где оно отсутствует.
  2. Сократите каждый из этих файлов только до столбца «Болезнь».
  3. Создайте файл со столбцом «ID» для каждого PHENO.
  4. Вставьте вместо того, чтобы объединить теперь уменьшенные файлы с идентификатором столбца. (Они отсортированы, и строки должны совпадать с пунктом 1)
  5. Создайте скрипт для распараллеливания.

Как:

  1. +2. Через awk-скрипт назовите его, например. fillrows.awk

    NR>1 {disease[$1]=$2}
    END {print FILENAME
         for (i=1;i<=15000;i++) {
           if (disease[i]!="")
              {print disease[i] > FILENAME"_red"}
           else {print "1" > FILENAME"_red"}
         }
    }
    

Это сделает файл chr1_A похожим на

 ID Disease
 2  0
 5  1

Кому chr1_A_red (показано не более 6 строк)

 chr1_A
 1
 0
 1
 1
 1
 1

При выполнении как: awk -f fillrows.awk chr1_A

  1. ID-столбец всегда один и тот же

    { echo ID ; seq 1 15000 ;} > ID_col
    
  2. Вставить вместе — это может быть ограничено в зависимости от оперативной памяти:

    for PHENO in {A..M} ; do
      paste ID_col chr*_$PHENO > $PHENO
    done
    
  3. Некоторые распараллеливания через параллель gnu

     #!/bin/bash
     ##get chrX-Y list without PHENO
     find -name 'chr*' | sed 's/_.$//' | sort -u > chrlist
     parallel awk -f rowfill ::: chr*{A..M}
     { echo ID ; seq 1 15000 ;} > ID_col
     parallel paste ID_col '{1}_{2}_red' '>' '{2}' :::: chrlist ::: {A..M}
    
1
FelixJN 8 Июн 2021 в 11:42

Я придумал рекурсивную функцию для объединения произвольного количества файлов:

join_all() {
    local -a join_opts
    local arg
    while :; do
        arg=$1
        shift
        [[ $arg == '--' ]] && break
        join_opts+=("$arg")
    done

    if (($# == 1)); then
        cat "$1"
    else
        join "${join_opts[@]}" "$1" "$2" | join_all "${join_opts[@]}" -- '-' "${@:3}"
    fi
}


for PHENO in A B C D E F G H I J K L M
do
    files=()
    # use brace expansion to generate the list of files
    files+=( chr{2,3,5,11,14,20,21,22,6,9,13}_${PHENO} )
    files+=( chr{18,8,17}-{1,2}_${PHENO} )
    files+=( chr{1,7,15,16}-{1,2,3}_${PHENO} )
    files+=( chr{19,4,10,12}-{1,2,3,4}_${PHENO} )

    join_all -a1 -a2 -e 1 -o auto -- "${files[@]}" > ${PHENO}
done
1
glenn jackman 8 Июн 2021 в 17:06