elemek/számok megszámlálása egy fileban

Sziasztok,

Van egy ilyen file formátumom (sokkal több számmal):

3,30,45,67,70
19,31,35,40,63
4,8,33,38,80
12,22,71,76,87
32,45,47,74,85
23,39,42,48,61
12,38,58,60,87
2,28,49,60,74
30,47,57,68,77
2,13,17,29,37
7,19,26,56,63
 

Szeretném benne megkeresni, hogy 1-30 -ig a számok hányszor fordulnak elő.

Eddig itt tartok:

szamoloFile = open("source.txt", "r")
data = szamoloFile.read()

for k in range(1,30):
    kiszamolt = data.count(k)
    print(k, kiszamolt)

Viszont erre kapok egy hibát:

kiszamolt = data.count(k)
TypeError: must be str, not int

Jó volna, ha az 1-s szám keresésnél az 10,11,12,13 stb nem lenne találat és így tovább a 2-3-4-5 stb számoknál.

 

Ötlet esetleg?

 

Köszönöm

a.

Hozzászólások

Szerkesztve: 2021. 11. 19., p – 10:44

A vesszők mentén split(',') metódussal a sort szétszabdalni, az így kapott rész sztringeket int(x) módon számmá alakítva összehasonlítani.

#!/usr/bin/python3

hanyszor = 0
for sor in open("source.txt", "r"):
    for oszlop in sor.split(','):
        szam = int(oszlop)
        if szam >= 1 and szam <= 30:
            hanyszor += 1

print("1..30 közötti számok:", hanyszor)

Bízom benne, hogy megérted a logikáját. A további segédprogikat ez alapján már könnyebben tudod előállítani.

A Python lelki világát kicsit sem ismerem, de az nem opció, hogy nem sztringműveletekkel dolgozod fel a fájlt, hanem parse-olod integerként a számokat belőle? Utána már megszámolhatod őket akárhogy.

Köszi!
Kicsit benéztem, de így most müxik.
Viszont az egyjegyűt megtalálja a kétjegyűben is.
Az a nagy kérdés, hogy az egyjegyű 1-9ig-t, ne találja meg a kétjegyű 10-30-ig ban.

szamoloFile = open("source.txt", "r")
data = szamoloFile.read()

for k in range(1,30):
k = str(k)
kiszamolt = data.count(k)
print(k, kiszamolt)

Szerintem még mindig stringként dolgozod fel az állományt. 

k = str(k) helyett: k = int(k)

Nekem hibátlan az alábbi kóddal:

f = open("source.txt", "r")
elofordulas = 0
for sor in f:
    for oszlop in sor.split(','):
        szam = int(oszlop)
        if szam >= 1 and szam <= 30:
            elofordulas += 1

print("1..30 közötti számok előfordulása (incl. 30 and 1):", elofordulas)

“The basic tool for the manipulation of reality is the manipulation of words. If you can control the meaning of words, you can control the people who must use them.”

― Philip K. Dick

cat szamok | tr ',' '\n' | sort -n | uniq -c

:)

Csak azért nem kn +1, mert ez azt feltételezi, hogy

- nincsenek a listában negatív számok, vagy 0

- valamint minden természetes szám megvan 30-ig

Ha fentiek a kiírásban benne voltak, akkor jár érte a k n +1 (Újraolvastam és értelmeztem a dolgokat. Az első feltétel hanyagolható, de a második csak jó eséllyel, azaz igen nagy számosságú sor esetében.)

Nem jó, mert a sort| uniq -c után a vizsgált számok nem lesznek sorrendben 1 és 30 közt. Ahhoz kéne egy újabb sort, amit viszont a második mező alapján kéne csinálni, mert ez tartalmazza a számot. ha az eredmény után egy | sort -k 1.8 -n szűrőt is beiktatunk, akkor már a vizsgált számok sorrendjében lesz sorrend, de a head -n 30 nem fog jó outputot adni, ha nincs minden 1-30 szám a vizsgált inputban.

$ cat szamok.txt | tr "," "\012" | sort -n | uniq -c | sed '0,/30$/I!d'

----------
Were antimatter present, its detection would be quite simple and straightforward. The most rudimentary detector suffices: simply place it down and wait. If the detector disappears, antimatter has been discovered - get out fast!

szamoloFile = open("szamok")
eredmenyek = []
for i in range(30):
    eredmenyek.append(0)
for line in szamoloFile:
    szamsor = line.strip().split(',')
    for szam in szamsor:
        index = int(szam)
        if index >= 1 and index <= 30:
            eredmenyek[index -1] += 1

for i in range(30):
    print(f"szám: {i+1}, előfordulás: {eredmenyek[i]}")
 

Szerkesztve: 2021. 11. 19., p – 11:07

És ebbe is be lehet kötni, nem zártam le a file-t :)

Teljes statisztika

awk -F\, \
'{
	for ( i= 1; i<=NF;i++)
		C[$i]++
}
END {
	for ( i= 1; i<=90;i++)
		print(i,C[i])
}'

A megoldás

awk -F\, \
'{
	for ( i= 1; i<=NF;i++)
		if ( $i <= 30 )
			C[$i]++
}
END {
	for ( i= 1; i<=30;i++)
		print(i, i in C ? C[i] : 0 )
}'

OT

A , önmagában semmilyen elterjedt shellben nem speciális karakter, így nem kell takarni

És ha a nyitó aposztrófot felviszed az első sorba, akkor ott is elhagyhatod a backslash-t - legalábbis a Bourne-szintaxisú shellek esetén

/OT

(Amúgy én is asszociatív (*) tömbre gondoltam, bár én ksh-ban.)

(*) awk-ban minden tömb hash/dictionary

Köszönjük Emese! ;)

És így rögvest 3 egész karakter leírásától megkímélted a publikumot. Ez igazán komoly teljesítmény! Ha egy sorban írod le (no lám, ez se jutott az eszedbe), akkor még rosszabbul olvasható. A "takarás" (már megint tanultam valamit) csak az ujjaimból jött, mert általában pipe szeparátoros adatokkal szoktam dolgozni. A program működik, ez a lényeg és nem más.

Igazi szakértelemre vall, ha 40M adatot shellben akarsz feldolgozni. Bár az awk tényleg asszociatív tömböt használ, itt viszont csak egy 90 elemű string vektorról van szó. Ezt kifejezetten lassítja az asszociatív tömb. Helyette néhány nagyságrenddel gyorsabb a feldolgozás, ha típus nélküli 2 bájtos indexként használod a számokat. No, ennyit a pythonról. ;)

Szerintem nem vetted észre a következőket:

-- OT /OT jelző

-- pont hogy azért javasoltam amit javasoltam, hogy olvashatóbb legyen (a te saját ködodhoz javasolt javításod még saját bevallásod szerint is olvashatatlanabb, de ha ez lenne a cél, akkor javasolnám, hogy dobd ki az aposztrófokat és mindenhova tegyél \-t )

Cserébe, szerinted ha egy példaprogramnál AWK-val asszociatív tömb segítségével dolgozol fel 40 MB adatot az pöpec, ha én leírom, hogy ugyanezt kb ugyanígy ksh-ban gondoltam, az már elvetendő, sőt egyenesen a szakmaiságot hiányolod belőlem.

Javasolnám a nicked javítását. Elég lenne csak a középső betűt a talpára állítani.

Kiegészítés:

azt kérlek magyarázd meg, hogy melyik AWK implementáció fogja neked kioptimalizálni a hast-t 90-elemú sztringvektorrá

Az offtopic offtopicja:

letöltöttem a teljes magyarországi ötös lottóhízások eredményét tartalmazó CSV-t. Mivel abban dátumok meg nyereményösszegek, stb. is szerepel a nyers számokon kívül, ráadásul dosos sorvégjellel, ezért egy nagyon picit letisztáztam. Aztán kopipészttel kimásoltam B úr szakmai szintű AWK-snipettjét, egy cseppet javítottam rajta (a letöltött fájl semicolon separated és nem comma separated), és lefuttattam. Aztán azt a szakmai mételyt is, amit én követtem el - mint írtam, Korn-shell script képében.

Meghajlok nagysága előtt:

0,03 real áll szemben az én tragikus 0,06-ommal szemben.

Ja, azt csak halkan jegyzem meg, hogy a teljes CSV - adattisztítás előtt - 217468, adattisztítás után pedig 48961 teljes bájt, szóval tényleg elképzelni is szörnyű, hogy mekkora előny lenne. ha vagy 80 MB-ot kéne végignyálazni - ráadásul mondjuk naponta 10x. Micsoda mázli, hogy nem kell :-)

Szoveges fileok feldolgozasaban verhetetlen. Egesz bonyolult adatstrukturakkal is elbanik.

Ha egyszer rakap az ember, nehez lejonni rola :) Nekem ez meg egyetem alatt sikerult. Valameyik info/programozas targy kereteben volt tema egy formalis leiro, ami egy az egyben AWK. Csak emlekeznek ra, hogy mi volt a neve, vagy mi volt a konyv cime...

Annak örülök, hogy mindig meglátod a lényeget! :-D

A ksh általában nem adatfeldolgozásra használatos. Erre a feladatra az awk sem az igazi - csak ennyi.

Ha OT, ha nem OT, komolytalan dolog ilyen hülyeségekbe belekötni.

azt kérlek magyarázd meg, hogy melyik AWK implementáció fogja neked kioptimalizálni a hast-t 90-elemú sztringvektorrá

Nem fogom elmagyarázni, hiszen ilyet nem állítottam.

néhány nagyságrenddel gyorsabb a feldolgozás, ha típus nélküli 2 bájtos indexként használod a számokat

Ilyen a C, asm, stb.

Jézusmáriaszentjózsef! Csak nem OV rejtőzködik a nick mögött? ;)

Bizony, ha így fogod fel, akkor beszóltam. Kinek? Hát annak, aki offtopic köntösbe bújtatva kritizál egy olyan programot, ami egyébként formailag hibátlan, a topicnyitó kérdését maradéktalanul lefedi és pontosan működik. Az ilyen tevékenység nem offtopic, nem kekeckedés, hanem felesleges.

Ezt a megoldást csak azért küldtem, mert feljebb a string vagy numerikus témakör is problémát okozott egyeseknek. Az awk pont jó ebben, de ezt inkább C-ben oldanám meg, mert a topicnyitó állítása szerint: "előfordulásokat most vizsgáljuk így 44 millió példán".

No, a futásidőre is láthattunk egy sikeres ellenpéldát kb. 3380 adatsor felhasználásával. ;) (Ha nem érted: lottóhúzás 1957 óta van vs. "letöltöttem a teljes magyarországi ötös lottóhízások eredményét tartalmazó CSV-t") Tudod, az ifjú titánokat - "most teszteltem le a programot pécén 1,5 M adattal" - már régen is elhajtottam: nekem 10 M adattal kellett futtatnom AIX alatt. Almát a körtével!

Ami meg a későbbiekben sem volt világos - íme egy vektorban számlálás (részlet, C):

ix=0;
for (i=0;i<Count;i++)
{
        if ( adat[i] >= '0' )
        {
                ix<<=8;
                ix+=adat[i];
        }
        else
        {
                tomb[rindex[ix]]++;
                ix=0;
        }
}

ahol

- Count a adat[]-ba beolvasott karakterek száma== adatfájl (unix text, mit a topicnyitóban)

- rindex a reverz index, pl.: rindex[0x0000000000003339]=39

- tomb a 91 elemű számláló vektor

Fogadjunk, hogy ez shell, awk, python stb-hez képest nagyságrendekkel gyorsabban fut! Ráadásul stringből dolgozik, amit nem kell átalakítani az indexeléshez. Az ilyen "sort" futásideje mindig a legkisebb.

A program meg olyan egyszerű, hogy nem is érdemes assemblerben megírni. ;)

Nézd, nekem AIX alatt 18 évig futott több, országos hatókörű rendszerem. A "munkásságomért" vezérigazgatói elismerést is kaptam. Jelenleg egyetlen automata rendszerem fut linux alatt - 12 éve készült, napi 80M processzt futtat és ~60 TB (35 M chunk) adatot kezel. Tán hasonlót se láttál shellben. ;)

Hidd el, az elmúlt közel 30 évben én is tettem le valamit az asztalra! Na és?

Nem kötök bele olyanba pl., hogy  "A program egyébként jó (bár ezt nem mondom), de reszketett a kezed és rossz színű az egyik SPACE, amit írtál."

Ha ilyet tennék, akkor én is megérdemlem. A "köszönjük Emese" == feleslegesen szóltál egy álproblémáról.

Zahy barátunknak, aki "oly sokat letett már az asztalra", nem tűnt fel, hogy "pascal stílusban" és nem a C programozók ízlése szerint írom a programokat. Létezik olyan utility, amivel oda-vissza lehet az ilyeneket alakítani.

Az "awk -F, 'BEGIN {..." nem praktikus. Egy komolyabb program gyakran így kezdődik:

awk -F, \

-v ... \

-v ... \

...

'BEGIN ...

Az első sorban megadott szeparátor miatt azonnal látod a feldolgozott formátumot - amit az IFS megadásánál esetleg keresni kell.

Utána áttekinthető módon sorakoznak az átadott változók.

Végül a program.

A -F\, literálás felesleges. Bár, ha olyan karakterre cseréled a szeparátort, ahol nem felesleges, akkor csak egy karaktert kell cserélni. Bizony, néhány száz program után kialakul a stílus - már, ha nem szeretsz sokat szívni. ;) Pl. a fent említett 12 éve készült főprogramban a 4800 sorból 46-ban szerepel az awk és ebből 38 tartalmaza -F\| szeparátort.

Mindezekbe bele lehet kötni, de minek.

Tanulni sosem késő, és te magad írtad, hogy "tanultál valamit" (persze érezhetően ezt is felesleges tudásként aposztrofáltad). Ugyan nem pont arra utaltál, amire én eredendően felhívtam a figyelmet (bárkiét, nem csak és kizárólag a tiédet). Úgyhogy nem volt felesleges.

Azóta is ragozod, hogy miért azt és úgy írtad, amit és ahogyan. Én viszont azt tapasztaltam, hogy egy kód olvashatóságát nagyon sok módon lehet rontani, és pl. a *ix világban a \ erőltetése - szerintem - ezek közé tartozik. Ha neked ez az infó megvan, és ennek ellenére így írod, lelked rajta. Én meg túl sok kezdőnél futottam már bele abba, hogy nem értik.

Az viszont furcsa szakmaiság, hogy a 40 KB-nyi adat feldolgozására hibás felvetésnek tekinted a ksh-t, de az AWK-t annyira nem, hogy még kódot is küldesz be hozzá (majd leírod, hogy igazából az se jó). Ezen gondolatmenet alapján vártam volna egy Perl-kódot, mert ugye az már erre is jó.

De morogj nyugodtan tovább.

Amit tanultam, az a "takarás", amit 1990 óta még egyszer sem hallottam. Az indoklást - miért írom így a programot - egyész pontosan leírtam feljebb. Szerintem egy 4-8000 soros, több száz automatát futtató adatfeldolgozó programban néhány backslash a kezdők legnagyobb problémája. ;) Automatikusan beleírtam, majd gondoltam, hogy mégis maradhat. Indoklást erre is írtam feljebb.

A 40 kB adat helyett itt 484..704 MB adatról van szó. Egy mai gépen durva becslés szerint 1 perc futásidő várható. (awk) Szerintem a shell ennél lassabb lehet. De ez mindegy. Az awk-t azért dobtam be, mert a korábbi hozzászólásokban több helyen is problémát okozott a típus. Ennél mindegy.

Több évtizedes rutin alapján kb. 1 M (ennél csöppet bonyolultabb szerkezetű) sor feldolgozására tartom az awk-t alkalmasnak. Felette C kell, ami legalább 6x gyorsabb.

Az awk programra zeller megoldása a legegyszerűbb Ezzel a futásidő 69s, 44 M sor lottószám szerű (609 MB) adaton. A vázolt C program-hoz hasonló megoldás futásideje 0,784 s, azaz 88x gyorsabb. Innentől teljesen felesleges azon vitatkozni, hogy melyik interpreteres és típusos nyelv a gyorsabb, mert itt csak bájtok vannak.

Bár elismerem, hogy ez a megoldás nem csak szakmaiatlan, de ronda is.

Bár mindkét program tartalmaza ugyanazt a felesleges vizsgálatot. Ti. a lottószám mindig >0.

A futásidejük 80 s, míg az én verzióim 37,5 s és 45 s - így már nem is annyira szellemes. ;)

Úgy látszik, a felesleges backslash lejtése miatt gyorsabban csúsznak bele a programba a bájtok. :-D

Vagy az egyszerűbb rekord nem váltja ki a +176 M getline() és (egyszerűbb) split() futásidejét.

Loopolgatás helyett megmérhetnéd 44 M adatsoron a ksh-t is!

Szellemes, ugyanis az egyetlen AWK-kód itt, amiben legalább halványan felvillan valami ennek a nyelvnek a sajátosságából. (A többi az kb Basic-ben is lehetne, annyira semmitmondó.) Szellemes =/= leggyorsabb, nem is ezt kérte a felvető.

Nem tudom miért kellene bármit lemérjek a 44 M (bájtra gondolsz vagy mire?) adatsoron, azt egyedül te keverted ide. (*)

Szerintem ott tévedtél el, hogy a senki (még én sem!) kritizálta a fantasztikus kódodat, egyszerűen hozzáfűztem egy megjegyzést, amit te személyed elleni sértésként könyveltél el, hiszen a kódod tökéletes, a hozzáfűznivalóm meg fölösleges. Hidd ezt. Én meg kiszállok, mert mostanra uncsi lettél.

(*) valószínűleg életem első unixos gépén (Videoton VT-32x, Motorola 68K, 1 - 2 MB memória) is még röhögve emberi idő alatt futna az én ksh-sciptem is a kérdésfelvetésben szereplő mennyiségű adattal, ha valaki tenne rá ksh-t (bezzeg a te awk-d sokkal gyorsabb lenne) - vagy egy-az-egyben átírná csh-ba a szintaktikailag különböző részeket. (Vagy az akkori idők XT-jén, az MKS Toolkitben levő ksh-val.) Eredeti Boune-shell eleve nem rúg labdába, hisz nincs neki beépített aritmetikai művelete, ezért egy rohadt inkrementálásért fork-exec-expr kell. Nincs semmilyen tömbkezelése, szóval lehetne szarozni az eval v_$index=$a tipusú dolgokkal és így tovább. bash-ra direkt finomítani kellett, mert szórakozottan print-et írtam echo helyett.

De neked ezt nem  mondhatom, mert te ezt már tudod, másnak meg nem mondhatom, mert a te egyik hozzászólásodra válaszoltam, így ki szeretnéd sajátítatni még ezt a szálat is. Legyen.

Báj-báj

Random számok eloszlásának vizsgálata apa és 10 éves gyerek vitájában c. tantárgy, a lottószám generátor kész, az előfordulásokat most vizsgáljuk így 44 millió példán.

/Írta a topicnyitó./

A kérdésfelvetésben csak formátum volt, mennyiség nem. Ha a formátumból 44 M példát veszünk, az 44 M sor x 5 szám. Ezt talán azért választották ennyire, mert az ötöslottó kombinációinak a száma 43 949 268.

(*) Alattunk két emeletel fejlesztették - a 80-as évek végén. ;) Sajnos ilyenem nincs, de van 1996-os Atlas 604 (Motorola, 100MHz PowerPC 604) és 2004-es p615 (1GHz POWER 4+). Egyiken sem fog fogyasztható sebességgel futni. A 2004-es POWER 5 már gyorsabb. A teszteket Core i3 4 gen és 1 vcpu Xeon VPS-en futtattam, kb. egyformák voltak.

Az, hogy lottószámgenerátor adathalmaza, az az eredeti kérdésfelvetésben még nem volt definiálva.

(*) A kutya nevű szerver (az már az ikertorony kivitel) még megvan egy ismerősömnél, de szerintem nem működőképes - már 20 éve se volt az amikor a mi cégünknél állt a sarokban.

BEGIN {
    RS="[,\n]"
}

{
    if (($1>=1) && ($1<=30)) myArray[$1]++
}

END {
    for (i=1; i<=30; i++)
        print myArray[i]
}
 

 

 

---- szerk

Ha ignoralnad azokat, a szamokat, amelyekbol nem fordult elo egy se:

END {
    for (i in myArray)
        print i, myArray[i]
}
 

$ for Y in {1..100}; do for X in {1..100}; do NUM=$(($RANDOM % 100 + 1)); echo $NUM; done | paste -sd,; done | tr , "\n" | sort | uniq -c  | awk '$2 <= 30' | sort -rn -k2
    103 30
     97 29
    106 28
    106 27
     85 26
    111 25
    114 24
    106 23
    108 22
     88 21
     92 20
     98 19
    106 18
    105 17
     82 16
     92 15
    114 14
     97 13
     79 12
    110 11
     94 10
     90 9
    108 8
     93 7
     80 6
     83 5
    105 4
     82 3
     92 2
     98 1
Szerkesztve: 2021. 11. 19., p – 14:03
from collections import Counter
from re import split

print(Counter(split("[\s,]", data)))

Try it online

 

Persze lehet szépíteni, ha akarod: sorba rendezni, számmá alakítani, meg amit még akarsz.

Ha már nyelvek, gforth-ban vagy pforth-ban esetleg yforth-ban esetleg valaki?

Szerintem furorban lenne a legegyszerűbb. Csatolom a legújabb build-beli megvalósítást, ami nagyon egyszerű. De ez csak kezdőknek ajánlott mert amúgy más nyelvekben nehezebb.

###sysinclude c:\Függvények.uh
tick sto startingtick
#g 100000 sto MAX
@MIN non !lippitik 'input.txt'
@MAX mem !maximize sto uniqueNumbers
&@˘=\jogszabaly+:!read:
donk ple &%=@no?non
one count
2 0 sto#s uniqueNumbers
2 @MAX maximum: {{ ,
@count {{
{{}}§jogszabály {{}} primeNumbers[] !/ inv { {{<}}§maximum }
}}
// {{}} gprintnl  // A talált maximum kiiratásához kommentezzük ki e sort de akkor a többi se fut le
{{}} @count++ sto#s#n
}}
@maximum inv mem -n " \o/ yay
."Time : " tick @startingtick - print ." tick\n"
."Maximum = " @count printnl
end
{ „MAX” } { „startingtick” } { „primeNumbers” } { „count” } & < # ne nyúlj hozzá mert nem működik akkor

Nem ismerem én se ezt a nyelvet, de tényleg agyráknak tűnik. Nem is értem, hogy ilyen nyelveket minek találnak ki, kin segít ez, ki hajlandó ezt megtanulni. Előnyét nem látom, csak olvashatatlan. Akinek ilyesmire van igénye, az a smalltalk, brainfuck, lisp, agda, stb. nyelvektől már eddig is megkapta. Aki meg normális nyelvet akar, annak meg ott a C vagy annak valami szintaxisbeli modernebb rokona, sokkal olvashatóbb szintaxissal, több libbel, hegyekben álló szakirodalommal és kézikönyvvel, sokkal több userrel a háta mögött.

Én egyébként a topikbeli problémának POSIX shellscript-tel mennék neki. Bár születtek jó C-s megoldások a topikban, amik sebességre is optimálisabbak, de nem minden gépen van C fordító (elérhető, de a normi disztrók és OS-ek nem telepítik), meg nem minden platformra lehet bináris kódot terjeszteni, míg POSIX shell minden unixlike platforma elérhető, és még architekurális korlát sincs.

Első körben megkeresném a fájlban az összes számot egrep-pel, amit pipe-ban sort-tal és ezzel feltöltenék egy tömböt, majd a tömbbel újra végigfutnék a fájlon, de már egrep -c segítségével, az meg kiírná, hogy egyes tömbelemekre hány találat van. Bár ez is csak elképzelés, ha igény van rá, a hétvégén megírom konkrét kódként.

Windows 95/98: 32 bit extension and a graphical shell for a 16 bit patch to an 8 bit operating system originally coded for a 4 bit microprocessor, written by a 2 bit company that can't stand 1 bit of competition.”

Poliverzum direkt olyanra alkotta meg, ami nem hasonlit a ma hasznalt programozasi nyelvekre. Ahelyett, hogy az urben jatszodo sci-fi regenyeben csak megemlitene, hogy mellesleg milyen programozasi nyelvet hasznalnak a hosei, meg is alkotta, es irt hozza interpretert is.

Ahogy pl. Tolkien is megemlithette volna az elf nyelvet, de letre is hozta (vagy ahogy a Star Trekben is van Klingon letezo mesterseges nyelvkent, nem csak hivatkoznak ra, hogy ja, mellesleg az urlenyek nem angolul beszelnek).

A strange game. The only winning move is not to play. How about a nice game of chess?