GNU/Linux >> Znalost Linux >  >> Linux

Použijte awk k výpočtu frekvence písmen

Nedávno jsem začal psát hru, kde skládáte slova pomocí dlaždic s písmeny. K vytvoření hry jsem potřeboval znát četnost písmen v běžných slovech v anglickém jazyce, abych mohl prezentovat užitečnou sadu dlaždic s písmeny. Frekvence písmen je diskutována na různých místech, včetně Wikipedie, ale chtěl jsem si frekvenci písmen vypočítat sám.

Linux poskytuje seznam slov v /usr/share/dict/words soubor, takže už mám seznam pravděpodobných slov k použití. words soubor obsahuje spoustu slov, která chci, ale několik ne. Chtěl jsem seznam všech slov, která nebyla složená slova (žádné pomlčky nebo mezery) nebo vlastní podstatná jména (žádná velká písmena). K získání tohoto seznamu mohu spustit grep příkaz k vytažení pouze řádků, které se skládají pouze z malých písmen:

$ grep  '^[a-z]*$' /usr/share/dict/words

Tento regulární výraz se ptá grep aby odpovídalo vzorům, které jsou pouze malými písmeny. Znaky ^ a $ ve vzoru představují začátek a konec řádku. [a-z] seskupení bude odpovídat pouze malým písmenům a do z .

Zde je rychlá ukázka výstupu:

$ grep  '^[a-z]*$' /usr/share/dict/words | head
a
aa
aaa
aah
aahed
aahing
aahs
aal
aalii
aaliis

Další zdroje pro Linux

  • Cheat pro příkazy Linuxu
  • Cheat sheet pro pokročilé příkazy systému Linux
  • Bezplatný online kurz:Technický přehled RHEL
  • Síťový cheat pro Linux
  • Cheat sheet SELinux
  • Cheat pro běžné příkazy pro Linux
  • Co jsou kontejnery systému Linux?
  • Naše nejnovější články o Linuxu

A ano, to všechno jsou platná slova. Například „aahed“ je zvolání minulého času „aah“, jako při relaxaci. A "aalii" je hustý tropický keř.

Teď už jen potřebuji napsat gawk skript, který provede práci počítání písmen v každém slově a poté vytiskne relativní frekvenci každého nalezeného písmene.

Počítání písmen

Jeden způsob, jak počítat písmena v gawk je iterovat každý znak v každém vstupním řádku a počítat výskyty každého písmene a do z . substr funkce vrátí podřetězec dané délky, například jedno písmeno, z většího řetězce. Tento příklad kódu například vyhodnotí každý znak c ze vstupu:

{
    len = length($0); for (i = 1; i <= len; i++) {
        c = substr($0, i, 1);
    }
}

Pokud začnu globálním řetězcem LETTERS který obsahuje abecedu, mohu použít index funkce k nalezení umístění jednoho písmene v abecedě. Rozbalím gawk příklad kódu pro vyhodnocení pouze písmen a do z ve vstupu:

BEGIN { LETTERS = "abcdefghijklmnopqrstuvwxyz" }
 
{
    len = length($0); for (i = 1; i <= len; i++) {
        c = substr($0, i, 1);
        ltr = index(LETTERS, c);
    }
}

Všimněte si, že funkce index vrací první výskyt písmene z LETTERS řetězec začínající 1 na prvním písmenu nebo nulou, pokud nebyl nalezen. Pokud mám pole dlouhé 26 prvků, mohu pole použít k počítání výskytů každého písmene. Toto přidám do svého příkladu kódu pro zvýšení (pomocí ++ ) počet pro každé písmeno, jak je uvedeno ve vstupu:

BEGIN { LETTERS = "abcdefghijklmnopqrstuvwxyz" }
 
{
    len = length($0); for (i = 1; i <= len; i++) {
        c = substr($0, i, 1);
        ltr = index(LETTERS, c);
 
        if (ltr > 0) {
            ++count[ltr];
        }
    }
}

Relativní frekvence tisku

Po gawk skript počítá všechna písmena, chci vytisknout frekvenci každého písmena, které najde. Nezajímá mě celkový počet jednotlivých písmen ze vstupu, ale spíše relativní četnost každého písmene. Relativní četnost měří počty tak, aby písmeno s nejmenším počtem výskytů (například písmeno q ) je nastaveno na 1 a ostatní písmena se vztahují k této hodnotě.

Začnu počtem písmen a a poté tuto hodnotu porovnejte s počty pro každé z ostatních písmen b do z :

END {
    min = count[1]; for (ltr = 2; ltr <= 26; ltr++) {
        if (count[ltr] < min) {
            min = count[ltr];
        }
    }
}

Na konci této smyčky je proměnná min obsahuje minimální počet pro každé písmeno. Mohu to použít k poskytnutí měřítka pro počty k vytištění relativní frekvence každého písmene. Pokud je například písmeno s nejnižším výskytem q a poté min se bude rovnat q počítat.

Poté procházím každé písmeno a vytisknu ho s jeho relativní frekvencí. Každý počet vydělím min vytisknout relativní četnost, což znamená, že písmeno s nejnižším počtem bude vytištěno s relativní četností 1. Pokud se jiné písmeno objeví dvakrát častěji než nejnižší počet, bude mít toto písmeno relativní četnost 2. zajímají mě zde celočíselné hodnoty, takže 2.1 a 2.9 jsou pro mé účely stejné jako 2:

END {
    min = count[1]; for (ltr = 2; ltr <= 26; ltr++) {
        if (count[ltr] < min) {
            min = count[ltr];
        }
    }
 
    for (ltr = 1; ltr <= 26; ltr++) {
        print substr(LETTERS, ltr, 1), int(count[ltr] / min);
    }
}

Dáme vše dohromady

Nyní mám gawk skript, který dokáže počítat relativní četnost písmen ve svém vstupu:

#!/usr/bin/gawk -f
 
# only count a-z, ignore A-Z and any other characters
 
BEGIN { LETTERS = "abcdefghijklmnopqrstuvwxyz" }
 
{
    len = length($0); for (i = 1; i <= len; i++) {
        c = substr($0, i, 1);
        ltr = index(LETTERS, c);
 
        if (ltr > 0) {
            ++count[ltr];
        }
    }
}
 
# print relative frequency of each letter
   
END {
    min = count[1]; for (ltr = 2; ltr <= 26; ltr++) {
        if (count[ltr] < min) {
            min = count[ltr];
        }
    }
 
    for (ltr = 1; ltr <= 26; ltr++) {
        print substr(LETTERS, ltr, 1), int(count[ltr] / min);
    }
}

Uložím to do souboru s názvem letter-freq.awk abych jej mohl snadněji používat z příkazového řádku.

Pokud chcete, můžete také použít chmod +x aby byl soubor spustitelný samostatně. #!/usr/bin/gawk -f na prvním řádku znamená, že Linux jej spustí jako skript pomocí /usr/bin/gawk program. A protože gawk příkazový řádek používá -f k určení, který soubor má použít jako skript, potřebujete, aby visel -f takže spuštění letter-freq.awk v shellu bude správně interpretováno jako spuštění /usr/bin/gawk -f letter-freq.awk místo toho.

Skript mohu otestovat pomocí několika jednoduchých vstupů. Pokud například do svého gawk vložím abecedu script, každé písmeno by mělo mít relativní četnost 1:

$ echo abcdefghijklmnopqrstuvwxyz | gawk -f letter-freq.awk
a 1
b 1
c 1
d 1
e 1
f 1
g 1
h 1
i 1
j 1
k 1
l 1
m 1
n 1
o 1
p 1
q 1
r 1
s 1
t 1
u 1
v 1
w 1
x 1
y 1
z 1

Opakujeme tento příklad, ale přidáme další výskyt písmene e vytiskne písmeno e s relativní četností 2 a každé další písmeno jako 1:

$ echo abcdeefghijklmnopqrstuvwxyz | gawk -f letter-freq.awk
a 1
b 1
c 1
d 1
e 2
f 1
g 1
h 1
i 1
j 1
k 1
l 1
m 1
n 1
o 1
p 1
q 1
r 1
s 1
t 1
u 1
v 1
w 1
x 1
y 1
z 1

A teď můžu udělat velký krok! Použiji grep pomocí příkazu /usr/share/dict/words soubor a určete frekvenci písmen pro všechna slova napsaná výhradně malými písmeny:

$ grep  '^[a-z]*$' /usr/share/dict/words | gawk -f letter-freq.awk
a 53
b 12
c 28
d 21
e 72
f 7
g 15
h 17
i 58
j 1
k 5
l 36
m 19
n 47
o 47
p 21
q 1
r 46
s 48
t 44
u 25
v 6
w 4
x 1
y 13
z 2

Ze všech malých písmen v /usr/share/dict/words soubor, písmena j , q a x vyskytují nejméně často. Písmeno z je také poměrně vzácný. Není překvapením, že písmeno e je nejčastěji používaný.


Linux
  1. Jak používat Awk a regulární výrazy k filtrování textu nebo řetězců v souborech

  2. Jak používat Nginx k přesměrování

  3. Průvodce pro začátečníky koukáním

  1. Jak používat BusyBox na Linuxu

  2. Jak používám cron v Linuxu

  3. Použití grep vs awk

  1. Jak používat Su Command v Linuxu

  2. Externí proměnná v Awk?

  3. Jak používat Awk k tisku pouze řádků obsahujících 5 sloupců?