Как я могу подсчитать строки файлов с разными именами и записать результат в 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
снова?