¿Cómo contar las ocurrencias de cada personaje?

13

Por ejemplo, tengo un archivo 1.txtque contiene:

Moscow
Astana
Tokyo
Ottawa

Quiero contar el número de todos los caracteres como:

a - 4,
b - 0,
c - 1,
...
z - 0
Set-xx
fuente
44
De la respuesta aceptada, no está completamente claro, ¿quieres "A" y "a" distinguido o no? tu pregunta sugiere que lo hagas.
Jacob Vlijm

Respuestas:

20

Podrías usar esto:

sed 's/\(.\)/\1\n/g' 1.txt | sort | uniq -ic
  4  
  5 a
  1 c
  1 k
  1 M
  1 n
  5 o
  2 s
  4 t
  2 w
  1 y

La sedparte coloca una nueva línea después de cada personaje. Luego salimos sortalfabéticamente. Y por fin uniqcuenta el número de ocurrencias. La -ibandera de uniqse puede omitir si no desea insensibilidad a mayúsculas y minúsculas.

caos
fuente
3
Esto es brillante. Una advertencia adicional sería canalizar nuevamente la salida sort -k 2para enumerarlos alfanuméricamente.
tetris11
3
Este es el camino más corto, más comprensible pero desafortunadamente el más lento
c0rp
En Mac OS XI tuve que usar sed -e $'s/\(.\)/\\1\\\n/g'(ver también stackoverflow.com/a/18410122/179014 )
asmaier
Al orden por el número de ocurrencias (descendente): | sort -rnk 1. Y si se trata de archivos muy grandes, como yo, solo puede probar algunos miles de líneas para obtener un proxy para los recuentos reales:cat 1.txt | shuf -n 10000 | sed 's/\(.\)/\1\n/g' | sort | uniq -ic | sort -rnk 1
cpury
6

Un poco tarde, pero para completar el conjunto, otro enfoque de python (3), resultado ordenado:

#!/usr/bin/env python3
import sys

chars = open(sys.argv[1]).read().strip().replace("\n", "")
[print(c+" -", chars.count(c)) for c in sorted(set([c for c in chars]))]

A - 1
M - 1
O - 1
T - 1
a - 4
c - 1
k - 1
n - 1
o - 4
s - 2
t - 3
w - 2
y - 1

Explicación

  1. Lea el archivo, saltee espacios y regrese como "caracteres":

    chars = open(sys.argv[1]).read().strip().replace("\n", "")
  2. Cree un conjunto (ordenado) de exclusivos:

    sorted(set([c for c in chars]))
  3. Cuente e imprima la aparición de cada uno de los personajes:

    print(c+" -", chars.count(c)) for c in <uniques>

Cómo utilizar

  1. Pegue el código en un archivo vacío, guárdelo como chars_count.py
  2. Ejecútelo con el archivo como argumento por:

    /path/to/chars_count.py </path/to/file>

    si el script es ejecutable o:

    python3 /path/to/chars_count.py </path/to/file>

    si no lo es

Jacob Vlijm
fuente
5

Por defecto en el F ield S eparator (FS) es el espacio o pestaña . Como deseamos contar cada carácter, tendremos que redefinir el FS a cero ( FS="") para dividir cada carácter en una línea separada y guardarlo en una matriz y, al final dentro del END{..}bloque, imprimir sus ocurrencias totales con el siguiente comando :

$ awk '{for (i=1;i<=NF;i++) a[$i]++} END{for (c in a) print c,a[c]}' FS="" file
A 1
M 1
O 1
T 1
a 4
c 1
k 1
n 1
o 4
s 2
t 3
w 2
y 1

En {for (i=1;i<=NF;i++) a[$i]++} ... FS="" ...bloque simplemente dividimos los personajes. Y
en el END{for (c in a) print c,a[c]}bloque estamos haciendo un bucle para agrupar ae imprimir el carácter guardado en él print cy su número de ocurrenciasa[c]

αғsнιη
fuente
3

Haga un forbucle para todos los caracteres que desea contar y grep -ioúselos para obtener todas las ocurrencias del personaje e ignorar mayúsculas y minúsculas, y wc -lpara contar instancias e imprimir el resultado.

Me gusta esto:

#!/bin/bash

filename="1.txt"

for char in {a..z}
do
    echo "${char} - `grep -io "${char}" ${filename} | wc -l`,"
done

El script genera esto:

a - 5,
b - 0,
c - 1,
d - 0,
e - 0,
f - 0,
g - 0,
h - 0,
i - 0,
j - 0,
k - 1,
l - 0,
m - 1,
n - 1,
o - 5,
p - 0,
q - 0,
r - 0,
s - 2,
t - 4,
u - 0,
v - 0,
w - 2,
x - 0,
y - 1,
z - 0,

EDITAR después del comentario

Para crear un bucle para todos los caracteres imprimibles, puede hacer esto:

#!/bin/bash

filename="a.txt"

for num in {32..126}
do
   char=`printf "\x$(printf %x ${num})"`
   echo "${char} - `grep -Fo "${char}" ${filename} | wc -l`,"
done

Esto contará todos los caracteres ANSI de 32 a 126; estos son los más legibles. Tenga en cuenta que esto no utiliza ignorar mayúsculas y minúsculas.

La salida de esto será:

- 0,
! - 0,
" - 0,
# - 0,
$ - 0,
% - 0,
& - 0,
' - 0,
( - 0,
) - 0,
* - 0,
+ - 0,
, - 0,
- - 0,
. - 0,
/ - 0,
0 - 0,
1 - 0,
2 - 0,
3 - 0,
4 - 0,
5 - 0,
6 - 0,
7 - 0,
8 - 0,
9 - 0,
: - 0,
; - 0,
< - 0,
= - 0,
> - 0,
? - 0,
@ - 0,
A - 1,
B - 0,
C - 0,
D - 0,
E - 0,
F - 0,
G - 0,
H - 0,
I - 0,
J - 0,
K - 0,
L - 0,
M - 1,
N - 0,
O - 1,
P - 0,
Q - 0,
R - 0,
S - 0,
T - 1,
U - 0,
V - 0,
W - 0,
X - 0,
Y - 0,
Z - 0,
[ - 0,
\ - 0,
] - 0,
^ - 0,
_ - 0,
` - 0,
a - 4,
b - 0,
c - 1,
d - 0,
e - 0,
f - 0,
g - 0,
h - 0,
i - 0,
j - 0,
k - 1,
l - 0,
m - 0,
n - 1,
o - 4,
p - 0,
q - 0,
r - 0,
s - 2,
t - 3,
u - 0,
v - 0,
w - 2,
x - 0,
y - 1,
z - 0,
{ - 0,
| - 0,
} - 0,
~ - 0,
stalet
fuente
Si no desea ignorar el caso, elimine el idel grep. (en su pregunta tenía solo 3 en el resultado esperado)
stalet
Oh gracias. "{a..z}": ¿son todos símbolos de 'a' a 'z'? ¿Qué pasa con todos los símbolos imprimibles, cómo podemos designarlos sin enumerarlos
Set-xx
He actualizado mi respuesta con un ejemplo sobre cómo extender la búsqueda de todos los caracteres legibles
stalet
Esas son muchas llamadas a greptoda la entrada repetidamente.
200_success
3

Aquí otra solución (en awk) ...

awk '
        { for (indx=length($0); indx >= 1; --indx)
                ++chars[tolower(substr($0, indx, 1))]
        }
END     { for (c in chars) print c, chars[c]; }
' 1.txt | sort
  • Crea una matriz asociativa con cada carácter como valor de índice y el recuento como valor de matriz.
  • La acción FIN imprime la matriz.
Howard H
fuente
no es necesario cat file | awk '...': puedes decirlo directamente awk '...' file.
fedorqui
2

El siguiente perloneliner hará el recuento. Puse la expresión regular en el contexto de la lista (para obtener el número de coincidencias) y lo puse en el contexto escalar:

$ perl -e '$a=join("",<>);for("a".."z"){$d=()=$a=~/$_/gi;print"$_ - $d,\n"}' 1.txt
a - 5,
b - 0,
c - 1,
d - 0,
e - 0,
f - 0,
g - 0,
h - 0,
i - 0,
j - 0,
k - 1,
l - 0,
m - 1,
n - 1,
o - 5,
p - 0,
q - 0,
r - 0,
s - 2,
t - 4,
u - 0,
v - 0,
w - 2,
x - 0,
y - 1,
z - 0,
Sylvain Pineau
fuente
Para deshacerse de la coma final parece requerir una reescritura significativa:perl -Mfeature=say -e '$a=join("",<>);say join(",\n", map { sprintf("%s - %d", $_, ($d=()=$a=~/$_/gi)); } ("a".."z"))'
200_success
2

Aquí hay una solución usando Python:

#!/usr/bin/env python2
import collections, string
with open('1.txt') as f:
    input_string = f.read().replace('\n', '').lower()
    count_dict = collections.Counter(input_string)
    for char in string.lowercase:
        print char + ' - ' + str(count_dict[char]) + ','

Aquí hemos usado la clase collectionsdel módulo Counterpara contar el número de ocurrencias de cada carácter, luego, para imprimir, hemos usado el stringmódulo para obtener todas las letras minúsculas por la variable string.lowercase.

Guarde el script anterior en un archivo con el nombre que desee, por ejemplo count.py. Ahora desde el mismo directorio donde está guardado python count.pyel archivo, simplemente puede ejecutarlo para ejecutar el archivo, desde cualquier otro directorio use la ruta absoluta al archivo para ejecutarlo, es decir python /absolute/path/to/count.py.

heemayl
fuente
¿Podría por favor aclarar su solución? Quiero decir: crear el archivo nombre_archivo, poner este código, chmod + x etc. etc. etc.
c0rp
@ c0rp: hecho ....
heemayl
1

Hace un tiempo escribí un programa en C para hacer eso, porque lo necesitaba para mirar archivos grandes y producir algunas estadísticas.

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <sysexits.h>


inline static double square(double x)
{
    return x * x;
}


int main()
{
    static const unsigned distribution_size = 1 << CHAR_BIT;

    int rv = EX_OK;
    uintmax_t *distribution = calloc(distribution_size, sizeof(*distribution));

    {
        int c;
        while ((c = getchar()) != EOF)
            distribution[c]++;

        if (ferror(stdin)) {
            perror("I/O error on standard input");
            rv = EX_IOERR;
        }
    }

    uintmax_t sum = 0;
    for (unsigned i = 0; i != distribution_size; i++)
        sum += distribution[i];
    double avg = (double) sum / distribution_size;

    double var_accum = 0.0;
    for (unsigned i = 0; i != distribution_size; i++)
    {
        const uintmax_t x = distribution[i];

        printf("'%c' (%02X): %20ju", isprint((int) i) ? i : ' ', i, x);
        if (x != 0) {
            var_accum += square((double) x - avg);
            printf(" (%+.2e %%)\n", ((double) x / avg - 1.0) * 100.0);
        } else {
            var_accum += square(avg);
            putchar('\n');
        }
    }

    double stdev = sqrt(var_accum / distribution_size);
    double varcoeff = stdev / avg;
    printf(
        "total: %ju\n"
        "average: %e\n"
        "standard deviation: %e\n"
        "variation coefficient: %e\n",
        sum, avg, stdev, varcoeff);

    free(distribution);
    return rv;
}

compilar con (suponiendo que el código fuente reside en character-distribution.c):

cc -std=c99 -O2 -g0 -o character-distribution character-distribution.c

corre con:

./character-distribution < 1.txt

Si no tiene un compilador de C listo, instale GCC:

sudo apt-get install gcc build-essential
David Foerster
fuente
0

Solución similar a @heemayl, con código más estricto, que funciona en Python 2.7 y Python 3.

#!/usr/bin/python

import collections
import fileinput
import itertools
import string

count = collections.Counter(itertools.chain(*fileinput.input()))
print(',\n'.join('{} - {}'.format(c, count[c] + count[c.upper()])
                 for c in string.ascii_lowercase))

La primera declaración count = collections.Counter(…) hace todo el trabajo real.

  • fileinput.input() lee cada línea de la entrada, que puede canalizarse a través de stdin o como argumentos de línea de comandos.
  • * hace que considere un carácter a la vez en lugar de una línea a la vez.
  • count = Counter(…)cuenta las ocurrencias de cada personaje de manera eficiente, en una sola pasada, y almacena el resultado en la countvariable.

La segunda línea solo imprime los resultados.

  • '{} - {}'.format(c, count[c] + count[c.upper()]) for c in string.ascii_lowercase hace una lista de cada personaje y su cuenta.
  • print(',\n'.join(…)) lo pone en el formato deseado: uno por línea, separado por comas, pero sin coma en la última línea.
200_success
fuente
0

GNU awk 4.1

awk -iwalkarray '{for (;NF;NF--) b[$NF]++} END {walk_array(b)}' FS=
[A] = 1
[O] = 1
[w] = 2
[k] = 1
[y] = 1
[T] = 1
[n] = 1
[a] = 4
[o] = 4
[c] = 1
[s] = 2
[t] = 3
[M] = 1

Si tiene una versión anterior de GNU awk, puede usarla for (c in b) print c, b[c].

Steven Penny
fuente
0

Aquí está la respuesta usando ruby. Se realiza cambiando la cadena en una lista uniq de los diferentes caracteres y utilizando el método de conteo en cada uno de ellos.

#!/usr/bin/env ruby

String content = IO.read("1.txt")
content.split("").uniq.sort.each { |chr| puts( chr + ' - ' + content.count(chr).to_s) }
stalet
fuente