Quiero contar los caracteres A's T's C's G's N's y "-" en un archivo, o cada letra si es necesario, ¿hay un comando rápido de Unix para hacer esto?
command-line
unix
shell
characters
Kirstin
fuente
fuente
[System.IO.File]::ReadAllText("C:\yourfile.txt").ToCharArray() | Group-Object $_ | Sort Count -Descending
Get-Content "C:\eula.3082.txt" | % { $_.ToCharArray() } | Group-Object | Sort Count -Descending
Respuestas:
Si quieres algo de velocidad real:
Es un pseudo-one-liner increíblemente rápido.
Una prueba simple muestra que en mi Core i7 CPU 870 @ 2.93GHz cuenta con poco más de 600MB / s:
A diferencia de las soluciones que involucran la clasificación, esta se ejecuta en memoria constante (4K), lo cual es muy útil si su archivo es mucho más grande que su memoria RAM.
Y, por supuesto, con un poco de grasa en el codo, podemos reducir 0,7 segundos:
Las redes de poco más de 1.1GB / s terminan en:
A modo de comparación, probé algunas de las otras soluciones en esta página que parecían tener algún tipo de promesa de velocidad.
La solución
sed
/awk
hizo un valiente esfuerzo, pero murió después de 30 segundos. Con una expresión regular tan simple, espero que esto sea un error en sed (GNU sed versión 4.2.1):El método perl también parecía prometedor, pero me di por vencido después de ejecutarlo durante 7 minutos.
fuente
grep -o foo.text -e A -e T -e C -e G -e N -e -|sort|uniq -c
Hará el truco como un trazador de líneas. Sin embargo, se necesita una pequeña explicación.
grep -o foo.text -e A -e T -e C -e G -e N -e -
greps el archivo foo.text para las letras a y gy el carácter-
de cada carácter que desea buscar. También lo imprime un carácter por línea.sort
lo ordena en orden. Esto prepara el escenario para la siguiente herramientauniq -c
cuenta las ocurrencias duplicadas consecutivas de cualquier línea. En este caso, dado que tenemos una lista ordenada de caracteres, obtenemos un recuento ordenado de los caracteres que seleccionamos en el primer pasoSi foo.txt contuviera la cadena,
GATTACA-
esto es lo que obtendría de este conjunto de comandosfuente
-o
.Pruebe este, inspirado en la respuesta de @ Journeyman.
La clave es conocer la opción -o para grep . Esto divide la coincidencia, de modo que cada línea de salida corresponde a una sola instancia del patrón, en lugar de la línea completa para cualquier línea que coincida. Dado este conocimiento, todo lo que necesitamos es un patrón para usar y una forma de contar las líneas. Usando una expresión regular, podemos crear un patrón disyuntivo que coincida con cualquiera de los caracteres que menciona:
Esto significa "coincidencia A o T o C o G o N o -". El manual describe varias sintaxis de expresiones regulares que puede usar .
Ahora tenemos una salida que se parece a esto:
Nuestro último paso es fusionar y contar todas las líneas similares, que simplemente se pueden lograr con un
sort | uniq -c
, como en la respuesta de @ Journeyman. El género nos da una salida como esta:Lo cual, cuando se canaliza
uniq -c
, finalmente se parece a lo que queremos:Anexo: Si desea totalizar el número de caracteres A, C, G, N, T y - en un archivo, puede canalizar la salida grep en
wc -l
lugar desort | uniq -c
. Hay muchas cosas diferentes que puede contar con solo ligeras modificaciones a este enfoque.fuente
Una línea que cuenta todas las letras con Python:
... produciendo una salida amigable de YAML como esta:
Es interesante ver cómo la mayoría de las veces Python puede vencer fácilmente incluso a bash en términos de claridad de código.
fuente
Similar al
awk
método de Guru :fuente
Después de usar UNIX durante un par de años, se vuelve muy competente al vincular varias operaciones pequeñas para llevar a cabo diversas tareas de filtrado y conteo. Cada uno tiene su propio estilo: algunos como
awk
ysed
, otros comocut
ytr
. Así es como lo haría:Para procesar un nombre de archivo en particular:
o como filtro:
Funciona así:
od -a
separa el archivo en caracteres ASCII.cut -b 9-
elimina el prefijood
pone.tr " " \\n
convierte los espacios entre caracteres en líneas nuevas para que haya un carácter por línea.egrep -v "^$"
elimina todas las líneas en blanco adicionales que esto crea.sort
reúne instancias de cada personaje juntos.uniq -c
cuenta el número de repeticiones de cada línea.Lo alimenté "¡Hola, mundo!" seguido de una nueva línea y obtuve esto:
fuente
La
sed
parte se basa en la respuesta de @ Guru , aquí hay otro enfoque que utilizauniq
, similar a la solución de David Schwartz.fuente
[[:alpha:]]
lugar de.
ensed
para solo coincidir caracteres y no líneas nuevas.[[:alpha:]]
fallará si también está tratando de combinar cosas como-
, que se mencionó en la preguntased -e 's/[^ATCGN-]//g' -e 's/\([ATCGN-]\)/\1\n/g' foo | sort | uniq -c
. Sin embargo, no sé cómo deshacerme de las nuevas líneas allí: \Puedes combinar
grep
ywc
hacer esto:grep
busca en el (los) archivo (s) dado (s) el texto especificado, y la-o
opción le dice que solo imprima las coincidencias reales (es decir, los caracteres que estaba buscando), en lugar del valor predeterminado que es imprimir cada línea en la que estaba el texto de búsqueda encontrado en.wc
imprime los recuentos de bytes, palabras y líneas para cada archivo, o en este caso, la salida delgrep
comando. La-w
opción le dice que cuente palabras, y cada palabra es una ocurrencia de su carácter de búsqueda. Por supuesto, la-l
opción (que cuenta líneas) también funcionaría, ya quegrep
imprime cada aparición de su carácter de búsqueda en una línea separada.Para hacer esto para varios caracteres a la vez, coloque los caracteres en una matriz y repítelo:
Ejemplo: para un archivo que contiene la cadena
TGC-GTCCNATGCGNNTCACANN-
, la salida sería:Para más información, vea
man grep
yman wc
.La desventaja de este enfoque, como señala el usuario Journeyman Geek a continuación en un comentario, es que
grep
debe ejecutarse una vez para cada personaje. Dependiendo de qué tan grandes sean sus archivos, esto puede generar un impacto notable en el rendimiento. Por otro lado, cuando se hace de esta manera, es un poco más fácil ver rápidamente qué caracteres se están buscando y agregarlos / eliminarlos, ya que están en una línea separada del resto del código.fuente
uniq -c
También parece una mejor manera de obtener una salida bien formateada. No soy * nix guru, lo anterior es justo lo que logré reunir a partir de mi conocimiento limitado y algunas páginas de manual :)Usando las líneas de secuencia de 22hgp10a.txt, la diferencia de tiempo entre grep y awk en mi sistema hace que usar awk sea el camino a seguir ...
[Editar]: Después de haber visto la solución compilada de Dave, olvídate de awk también, ya que la completó en ~ 0.1 segundos en este archivo para contar con mayúsculas y minúsculas.
La versión insensible a mayúsculas y minúsculas de ghostdog se completó en ~ 14 segundos.
El sed se explica en la respuesta aceptada a esta pregunta .
La evaluación comparativa es como en la respuesta aceptada a esta pregunta .
La respuesta aceptada por ghostdog74 fue a esta pregunta .
fuente
s/cache[letters[x]]/cache[letters[x]]+cache[toupper(letters[x])]
minar para que no se distinga entre mayúsculas y minúsculas sin afectar su velocidad.Creo que cualquier implementación decente evita la clasificación. Pero debido a que también es una mala idea leer todo 4 veces, creo que de alguna manera se podría generar una secuencia que pasa por 4 filtros, uno para cada personaje, que se filtra y donde las longitudes de la secuencia también se calculan de alguna manera.
Las sumas acumulativas están entonces en tmp [0-6] .txt ... por lo que el trabajo aún está en progreso
Hay solo 13 tuberías en este enfoque, que se convierte en menos de 1 Mb de memoria.
Por supuesto, mi solución favorita es:
fuente
tr
.No sabía
uniq
ni sobregrep -o
, pero dado que mis comentarios sobre @JourneymanGeek y @ crazy2be tenían tanto apoyo, tal vez debería convertirlo en una respuesta propia:Si sabe que solo hay caracteres "buenos" (aquellos que desea contar) en su archivo, puede buscar
Si solo se deben contar algunos caracteres y otros no (es decir, separadores)
El primero usa el comodín de expresión regular
.
, que coincide con cualquier carácter individual. El segundo usa un 'conjunto de caracteres aceptados', sin un orden específico, excepto que-
debe ser el último (A-C
se interpreta como 'cualquier carácter entreA
yC
). En ese caso, se requieren comillas para que su shell no intente expandirlo para verificar los archivos de un solo carácter si los hay (y producir un error de "no coincidencia" si no hay ninguno).Tenga en cuenta que "sort" también tiene un
-u
indicador de nique, por lo que solo informa cosas una vez, pero no tiene un indicador complementario para contar duplicados, por lo queuniq
es obligatorio.fuente
-
no tiene que ser el último si se escapa con una barra invertida:'[A\-CTGN]'
debería funcionar bien.Una tonta:
tr
eliminar (-d
) todos los caracteres excepto (-c
) ATCGN-iconv
para convertir a ucs2 (UTF16 limitado a 2 bytes) para agregar un byte 0 después de cada byte,tr
para traducir esos caracteres NUL a NL. Ahora cada personaje está en su propia línea.sort | uniq -c
para contar cada línea uniqEsa es una alternativa a la
-o
opción grep no estándar (GNU) .fuente
El formato de salida no es el mejor ...
Teoría de operación:
La velocidad parece ser 60 MBps +
fuente
Archivo de muestra:
Mando:
fuente
Combinando algunos otros
Agregue
| sort -nr
para ver los resultados en orden de frecuencia.fuente
Respuesta corta:
Si las circunstancias lo permiten, compare los tamaños de archivo de los conjuntos de caracteres bajos con uno sin caracteres para obtener un desplazamiento y solo cuente los bytes.
Ah, pero los detalles enredados:
Esos son todos los personajes Ascii. Un byte por. Los archivos, por supuesto, tienen metadatos adicionales antepuestos para una variedad de cosas utilizadas por el sistema operativo y la aplicación que lo creó. En la mayoría de los casos, esperaría que estos ocupen la misma cantidad de espacio independientemente de los metadatos, pero trataría de mantener circunstancias idénticas cuando pruebe por primera vez el enfoque y luego verifique que tenga un desplazamiento constante antes de no preocuparse por ello. El otro problema es que los saltos de línea generalmente involucran dos caracteres de espacio en blanco ASCII y cualquier pestaña o espacio sería uno cada uno. Si puede estar seguro de que estos estarán presentes y no hay forma de saber cuántos de antemano, dejaría de leer ahora.
Puede parecer una gran cantidad de restricciones, pero si puede establecerlas fácilmente, esto me parece el enfoque más fácil / de mejor rendimiento si tiene un montón de estos para mirar (lo que parece probable si eso es ADN). Comprobar la longitud de una tonelada de archivos y restar una constante sería mucho más rápido que ejecutar grep (o similar) en cada uno.
Si:
Y dos cosas que tal vez no importen, pero las probaría primero
Intente encontrar el desplazamiento haciendo lo siguiente:
Compare un archivo vacío con uno con algunos caracteres fácilmente contables por humanos con uno con algunos caracteres más. Si restar el archivo vacío de los otros dos archivos le da recuentos de bytes que coinciden con el recuento de caracteres, ya está. Verifique las longitudes de los archivos y reste esa cantidad vacía. Si desea intentar descubrir archivos de varias líneas, la mayoría de los editores adjuntan dos caracteres especiales de un byte para los saltos de línea, ya que Microsoft tiende a ignorar uno, pero al menos tendría que buscar caracteres de espacio en blanco, en cuyo caso bien podrías hacerlo todo con grep.
fuente
Manera Haskell :
funciona así:
compilando y usando:
No es bueno para grandes archivos tal vez.
fuente
Hack perl rápido:
-n
: Itera sobre las líneas de entrada pero no imprime nada por ellas-l
: Elimina o agrega saltos de línea automáticamentewhile
: iterar sobre todas las apariciones de los símbolos solicitados en la línea actualEND
: Al final, imprimir resultados%a
: Hash donde se almacenan los valoresLos caracteres que no aparecen en absoluto no se incluirán en el resultado.
fuente