Como posso contar linhas de arquivos com nomes diferentes e gravar o resultado em um arquivo csv?

0

Estou escrevendo um script para analisar alguns dados. Eu tenho vários subconjunto de arquivos, e gostaria de contar a linha desses arquivos e gravar o resultado em um arquivo csv. Vou tentar fazer um exemplo. Eu tenho esses dois subconjunto de arquivos:

sample1.ext  
sample1.ext2  
sample1.ext3

sample2.ext  
sample2.ext2  
sample2.ext3

Gostaria de contar as linhas contidas em todos os arquivos em *.ext , *.ext2 e *.ext3 e gravar os resultados em um arquivo csv que pareça:

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

Após contar a primeira série de arquivos em *.ext , envio os resultados para a primeira coluna de um arquivo csv. Como escrevo a saída da segunda série de contagem de *.ext2 na segunda coluna do mesmo arquivo csv? E o mesmo para a terceira coluna?

Obrigado a todos pelas respostas, eu estava tentando adaptá-las aos meus arquivos, mas infelizmente não posso fazê-lo. O exemplo que eu postei foi apenas um exemplo, onde coloquei números em vez de extensões estranhas para ser mais fácil de entender o problema. Vocês todos entenderam, mas vocês se concentraram demais nos números que não existem na realidade. Eu vou te explicar novamente usando os arquivos reais. Esses arquivos estão vindo de um mapeamento de dados genômicos para um genoma de referência. Eu trato esses dados para limpá-los, então eu tenho três etapas em que o número de linhas é alterado. Então o arquivo é:

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

A extensão bam é um arquivo compactado. Para contar as linhas neste arquivo, existe uma linha de comando especial:

samtools view -c (file)

A única maneira que encontrei foi fazer uma iteração em cada *sort.bam , *mapped.bam , *rmdup.bam e gravar uma saída de texto para cada um e colá-los no final em um arquivo csv. Existe uma maneira de evitar esses três loop e fazer tudo juntos? Desculpe pelo mal-entendido, todos vocês têm ótimas ideias!

    
por Giulia L. 05.02.2016 / 17:58

3 respostas

1

Você pode usar este script 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")
}

Salve-o em algum lugar em seu sistema, torne-o executável e execute-o passando o diretório como um argumento:

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

O que o script faz é:

  • Itera todos os arquivos em path/to/directory ; se um nome de arquivo corresponder a .*\.(sort|mapped|rmdup)\.bam$ , anexa a sequência antes de .sort.bam , .mapped.bam ou .rmdup.bam a uma lista @names se ainda não estiver na lista;
  • Para cada nome na lista @names classificada como $name , acrescenta $name a uma lista @fields ; para cada extensão em sort , mapped e rmdup as $extension verifica se $name.$extension.bam existe em path/to/directory ; se o arquivo não existir, anexa 0 a @fields , imprime uma mensagem de erro e passa para o próximo $extension / $name ; se o arquivo existir, anexa a saída de <"$name.$extension.bam" wc -l a @fields ; uma vez que todos os valores possíveis para $extension foram iterados, imprime uma linha contendo os elementos de @fields unidos em , .
por kos 08.02.2016 / 14:31
1

Supondo que você queira uma saída como 42, 19, 10207, 3 em cada linha (sem nomes de arquivos), wc e alguns Bash ing resolverão seu problema.

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

Leia man bash , man wc , man seq e man bash novamente

Respondendo ao comentário:

Você leu as páginas man ?

$( seq 1 100) é substituído pelos resultados do comando seq 1 100 , que simplesmente exibe os números inteiros de 1 a 100 (que% de leituraman seq teria informado). Substitua-o por algo que forneça os números das amostras que você possui.

Coloque o código em um arquivo (por exemplo, test.sh ) e execute-o com bash -x test.sh para ver detalhes. Substitua o seq 1 100 por seq 1 2 para o teste, para evitar uma avalanche de saída.

samplenum contém o número da amostra, que, para este exemplo, é de 1 a 100.

sample , em sample${samplenum}.* é apenas uma string. Ele é concatenado com o valor de samplenum e a string .* para produzir o padrão de nome de arquivo, por exemplo, sample1.* na primeira vez através do for samplenum ... loop, sample2.* na segunda vez, etc.

Você leu e entendeu man bash , man wc , man seq e man bash novamente?

    
por waltinator 05.02.2016 / 19:56
1

Uma opção de python

Pergunta interessante. Uma boa ocasião para aplicar o groupby()

do python

Como seus arquivos estão em um único diretório "flat":

#!/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]))

Como funciona

Se um diretório contiver nove arquivos:

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
  • O script lista os arquivos, divide cada um dos nomes em duas seções, por exemplo:

    sample2
    

    e

    ext2
    

    desde que a ordem de ambas as linhas e o comprimento do arquivo dentro das linhas depende da classificação exata dessas duas seções.

  • O script então ordena os arquivos pela primeira seção do nome, já que o comprimento de cada um dos arquivos (com uma seção de nome similar) deve ser agrupado por primeira seção em uma linha; sample1 , sample2 , sample3 e assim por diante.
  • Subseqüentemente, os subgrupos (por csv line) são criados, classificados adequadamente pela seção de nome second , para fazer com que os números (line-) apareçam na ordem correta na linha

O resultado:

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

Como usar

  • Copie o script em um arquivo vazio, salve-o como create_csv.py
  • Execute-o com o diretório com seus arquivos como argumento

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

Nota importante

O método, usado para contar as linhas, é apropriado desde que os arquivos não sejam enormes . Se os arquivos forem , outro método para contar as linhas resultaria em um melhor desempenho.

EDITAR

Como resultado das informações mais recentes, adicionadas à sua pergunta, uma versão editada do script. Coincidentemente, pouco tem que ser mudado: o script divide os nomes dos arquivos pelo primeiro ponto encontrado, pelo comando:

item.split(".", 1)

Como a última seção do nome é .bam , que é igual em todos os arquivos , não tem sentido para a ordem de classificação.

Em seguida, só precisamos substituir a maneira "antiga" de contar as linhas do arquivo:

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

por (a implementação & integração de python no script de-) o comando que você forneceu:

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

O script editado

#!/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]))

Nota

Observe que a ordem dos números dentro da linha é determinada pela ordem classificada da segunda parte do nome, por exemplo,

mapped.bam, rmdup.bam, sort.bam  
    
por Jacob Vlijm 08.02.2016 / 17:43