¿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

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.


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


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]


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,

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.

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,

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.


¿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

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.

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].


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) }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.