Как я могу подсчитать строки файлов с разными именами и записать результат в CSV-файл?

Я пишу сценарий для анализа некоторых данных. У меня есть несколько подмножеств файлов, и я хотел бы посчитать строку из этих файлов и записать результат в CSV-файл. Я постараюсь сделать пример. У меня есть эти два подмножества файла:

sample1.ext  
sample1.ext2  
sample1.ext3

sample2.ext  
sample2.ext2  
sample2.ext3

Я хотел бы посчитать строки, содержащиеся во всех файлах в *.ext, *.ext2 а также *.ext3 и запишите результаты в CSV-файл, который выглядит так:

count(sample1.ext), count(sample1.ext2), count(sample1.ext3)  
count(sample2.ext), count(sample2.ext2), count(sample2.ext3)

После подсчета первой серии файла в *.extЯ выводить результаты в первый столбец файла CSV. Как мне записать вывод второй серии отсчета *.ext2 во второй столбец того же CSV-файла? И то же самое для третьего столбца?


Спасибо всем за ответы, я пытался адаптировать их к своим файлам, но, к сожалению, я не могу этого сделать. Пример, который я разместил, был просто примером, где я помещал числа вместо странных расширений, чтобы было легче понять проблему. Вы все поняли, но вы слишком сосредоточились на числах, которых нет в реальности. Я объясню вам снова, используя реальные файлы. Эти файлы поступают из отображения геномных данных в эталонный геном. Я обрабатываю эти данные, чтобы очистить их, поэтому у меня есть три шага, где меняется количество строк. Итак, файл:

name.sort.bam  
name.mapped.bam  
name.rmdup.bam  
othername.sort.bam  
othername.mapped.bam  
othername.rmdup.bam   

Расширение bam является сжатым файлом. Для подсчета строк в этом файле есть специальная командная строка:

samtools view -c (file)

Единственный способ, который я нашел, это перебирать каждый *sort.bam, *mapped.bam, *rmdup.bam и напишите вывод txt для каждого, и вставьте их в конце в файл csv. Есть ли способ избежать этих трех циклов и сделать все вместе? Извините за недоразумение, у всех вас есть отличные идеи!

3 ответа

Решение

Вы можете использовать этот Perl-скрипт:

#! /usr/bin/perl
use strict;
use warnings;

my @names;
my @files;

@ARGV == 1 || die();

opendir(my $dir, $ARGV[0]) || die $!;

while(readdir($dir)) {
    if($_ =~ /(.*)\.(sort|mapped|rmdup)\.bam$/) {
        grep(/^$1$/, @names) == 0 && push(@names, $1);
    }
}

close($dir);

foreach my $name (sort(@names)) {
    my @fields;
    push(@fields, $name);
    foreach my $extension ("sort", "mapped", "rmdup") {
        if(! -f "$ARGV[0]/$name.$extension.bam") {
            push(@fields, 0);
            print STDERR "'$ARGV[0]/$name.$extension.bam' missing\n";
            next;
        }
        my $count = `<"$ARGV[0]/$name.$extension.bam" wc -l`;
        chomp($count);
        push(@fields, $count)
    }
    print(join(", ", @fields)."\n")
}

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

path/to/script path/to/directory
% tree directory
directory
├── name.mapped.bam
├── name.rmdup.bam
├── name.sort.bam
├── othername.mapped.bam
├── othername.rmdup.bam
└── othername.sort.bam

0 directories, 6 files
% perl script.pl directory
name, 0, 0, 0
othername, 0, 0, 0
% for f in directory/*.sort.bam; do printf 'line\n' >>"$f"; done
% perl script.pl directory                                      
name, 1, 0, 0
othername, 1, 0, 0

Что делает скрипт:

  • Перебирает все файлы в path/to/directory; если имя файла совпадает .*\.(sort|mapped|rmdup)\.bam$, добавляет строку перед .sort.bam, .mapped.bam или же .rmdup.bam к списку @names если еще нет в списке;
  • Для каждого имени в отсортированном @names перечислить как $name, добавляет $name к списку @fields; для каждого расширения в sort, mapped а также rmdup как $extension проверяет, если $name.$extension.bam существует в path/to/directory; если файл не существует, добавляет 0 в @fields, печатает сообщение об ошибке и переходит к следующему $extension / $name; если файл существует, добавляет вывод <"$name.$extension.bam" wc -l в @fields; раз все возможные значения для $extension был повторен, печатает строку, содержащую элементы @fields присоединился к ,,

Опция Python

Интересный вопрос Хороший повод применить питона groupby()

Поскольку ваши файлы находятся в едином "плоском" каталоге:

#!/usr/bin/env python3
from itertools import groupby
import os
import sys

dr = sys.argv[1]
# list the files in the directory, split into "sortable" elements
flist = [[item, item.split(".", 1)] for item in os.listdir(dr)]
# sort the file list by first section (until the first found dot)
flist.sort(key=lambda x: x[1][0])
# create sub groups of the files, grouped by first section of name
for key, line in groupby(flist, lambda x: x[1][0]):
    line = list(line)
    # sort the files by second section of name for correct order in the csv lines
    line.sort(key=lambda x: x[1][1])
    # count the lines of the files, arrange the csv file
    print((", ").join([str(len(open(dr+"/"+f[0]).readlines())) for f in line]))

Как это устроено

Если каталог содержит девять файлов:

sample1.ext                  2 lines
sample1.ext2                 3 lines
sample1.ext3                 3 lines

sample2.ext                  1 lines
sample2.ext2                 1 lines
sample2.ext3                 4 lines

sample3.ext                  6 lines
sample3.ext2                 1 lines
sample3.ext3                 4 lines
  • Скрипт перечисляет файлы, разбивает каждое из имен на два раздела, например:

    sample2
    

    а также

    ext2
    

    поскольку порядок строк и длина файла внутри строк зависит от точной сортировки этих двух разделов.

  • Затем сценарий сортирует файлы по первому разделу имени, поскольку длина каждого из файлов (с аналогичным разделом имени) должна быть сгруппирована для каждого раздела в одну строку; sample1, sample2, sample3 и так далее.
  • Впоследствии подгруппы (за csv строки), правильно отсортированные по второму разделу имени, чтобы числа (строки) отображались в правильном порядке в строке

Результат:

python3 '/home/jacob/Bureaublad/create_csv.py' '/home/jacob/Bureaublad/samples' 
2, 3, 3
1, 1, 4
6, 1, 4

Как пользоваться

  • Скопируйте скрипт в пустой файл, сохраните его как create_csv.py
  • Запустите его с каталогом с вашими файлами в качестве аргумента

    python3 /path/to/create_csv.py /path/to/directory_with_files
    

Важная заметка

Метод, используемый для подсчета строк, подходит для тех случаев, когда файлы не очень большие. Если файлы есть, другой способ подсчета строк приведет к повышению производительности.


РЕДАКТИРОВАТЬ

В результате последней информации, добавленной к вашему вопросу, отредактированная версия скрипта. По совпадению, не так много нужно изменить: скрипт уже разделил имена файлов по первой найденной точке командой:

item.split(".", 1)

Так как последний раздел имени .bam, который одинаков для всех файлов, для порядка сортировки не имеет смысла.

Тогда нам нужно только заменить "старый" способ подсчета строк файла:

str(len(open(dr+"/"+f[0]).readlines()))

с помощью (реализация и интеграция в скрипт на языке-) команды, которую вы предоставили:

str(subprocess.check_output(["samtools", "view", "-c", dr+"/"+f[0]]).decode("utf-8").strip())

Отредактированный скрипт

#!/usr/bin/env python3
from itertools import groupby
import os
import sys
import subprocess

dr = sys.argv[1]
# list the files in the directory, split into "sortable" elements
flist = [[item, item.split(".", 1)] for item in os.listdir(dr)]
# sort the file list by first section (until the first found dot)
flist.sort(key=lambda x: x[1][0])
# create sub groups of the files, grouped by first section of name
for key, line in groupby(flist, lambda x: x[1][0]):
    line = list(line)
    # sort the files by second section of name for correct order in the csv lines
    line.sort(key=lambda x: x[1][1])
    # count the lines of the files, arrange the csv file
    print((", ").join([
        str(subprocess.check_output(["samtools", "view", "-c", dr+"/"+f[0]]).decode("utf-8").strip())
        for f in line]))

Заметка

Обратите внимание, что порядок чисел внутри строки определяется отсортированным порядком второй части имени, например

mapped.bam, rmdup.bam, sort.bam  

Предполагая, что вы хотите вывод, как 42, 19, 10207, 3 в каждой строке (без имен файлов), wcи немного BashИнг решит вашу проблему.

outfile="Result.csv" 
for samplenum in $( seq 1 100 ) ; do
    line=""
    for file in sample${samplenum}.* ; do
        numlines=$( wc -l <$file )
        line="$line $numlines,"
    done
    # remove the final comma
    line=${line%,}
    # not quoting $line below will suppress the initial blank 
    echo $line >> $outfile 
done

Читать man bash, man wc, man seq а также man bash снова

Отвечая на комментарий:

Вы читали man страницы?

$( seq 1 100) заменяется результатами seq 1 100 команда, которая просто выводит целые числа от 1 до 100 (что читает man seq сказал бы тебе). Замените его на то, что дает количество образцов, которые у вас есть.

Поместите код в файл (например, test.sh) и запустить его с bash -x test.sh чтобы увидеть детали. Заменить seq 1 100 с seq 1 2 для теста, чтобы избежать лавины вывода.

samplenum содержит номер выборки, который, для этого примера, работает от 1 до 100.

sample, в sample${samplenum}.* это просто строка. Объединяется со значением samplenum и строка .* создать шаблон имени файла, например sample1.* первый раз через for samplenum ... петли, sample2.* второй раз и т. д.

Вы прочитали и поняли, man bash, man wc, man seq а также man bash снова?

Другие вопросы по тегам