Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Python: Sanalistan pituus ei täsmää (dict, len)

Sivun loppuun

kayttaja-14784 [28.04.2019 11:51:16]

#

Tervehdys,

minulla on iso sanakirjatiedosto, jossa Pythonin len() funktion mukaan on 1625 avain-arvo paria. Kun kopioin saman tiedoston Word-asiakirjaan ja korvaan kunkin avaimen arvon jollakin toisella luvulla, saan koko sanaston kooksi 1807 sanaa. Tulos näyttää heittävän enemmän isommilla avaimen arvoilla kuten Python 803 vs. Word 926.

Ohjelmassa kiinnostaa nimenomaan tarkka tieto kunkin avaimen arvon kokonaisfrekvenssistä. Saaneeko sitä selville muuten kuin len() funktiolla.

Olisiko tietoa, miten tarkan arvon Pythonin len() funktio antaa isolla sanakirjatiedostoilla, koska eroa näyttäisi olevan verrattuna eri tavoin laskettuun tulokseen, jossa Word:llä saatu tulos lienee oikea? Käytössä on uusin Pythonin versio, mutta tulos oli sama hieman vanhemmallakin versiolla.

Grez [28.04.2019 12:07:51]

#

Millä perusteella arvioit Wordin tuloksen olevan oikea?

Nämä on taas näitä kysymyksiä, joihin olisi äärimmäisen yksinkertaista vastata, jos et tekisi vastaamista vaikeaksi.

Eli kerro millä perusteella tiedostossa oleva tieto pitäisi laskea avain-arvopariksi (eli millä saa "oikean" tuloksen) ja sitten laita joku pätkä tiedostosta (tai koko tiedosto) jossa saat eri tuloksen Pythonilla ja Wordillä. Näin pystymme kertomaan sinulle hetkessä, kumpi laskee eri tavalla kuin haluaisit ja mistä se johtuu.

Tai sitten voidaan kirjoittaa 100 sivua spekulaatiota.

lainaus:

Saaneeko sitä selville muuten kuin len() funktiolla.

Sanoisin että len() antaa aina täsmällisen tarkan arvon. Sitä et tuossa kertonut että miten sen sanakirjatiedoston luet Pythoniin, koska se lukemistapa ja tiedoston rakenne tietenkin vaikuttaa siihen, kuinka suureksi se muodostuu.

Siis, koodisi ei kuitenkaan ole tällainen: (koska se antaisi vain virheilmoituksen)

print(len(tiedosto.dic))

kayttaja-14784 [28.04.2019 13:07:40]

#

Tervehdys ja kiitos kommenteistasi,

teksti tuli varmaan muotoiltua liian epämääräisesti. Tein siis seuraavasti:

aluksi kysyin Pythonin len(y),funktiolla, jossa tiedoston nimi on y, mikä on tiedoston koko, joka oli se 1625 avain-arvo paria. Sitten kopioin koko tiedoston Word-asiakirjaan ja laskin montako sanaa tulee yhdelle sivulle ja laskin sanojen eli avain-arvo -parien määrän kertomalla sivumäärän sivulla olevien sanojen määrällä ja lisäämällä siihen vajaan sivun sanojen määrän. Tulos oli tämän mukaan 1807 paria.

Sitten käytin Word:n Korvaa toimintoa, jolla korvasin kunkin avaimen arvon, esim . 1>11 ja kirjasin korvausten määrän ylös ja laskin kaikkien korvausten määrän yhteen eri tapauksissa. Tulos oli taas 1807 paria.

Eli Wordin mukaan sanoja (avain-arvo pareja) on tiedostossa y kaikkiaan 1807 paria, mutta Pythonin mukaan vain 1625.

Siis mitään ohjelmallista toteutusta ei ollut Word-dokkarin luomiseksi Pythoniin vaan pelkkä vertaus siitä, minkä tuloksen Python antaa len(y) funktiolla ja Word teksturi samalle tiedostolle ja samalle sanastomateriaalille.

Metabolix [28.04.2019 13:38:02]

#

Jos oikeasti kirjoitat vain len(tiedoston_nimi), saat tiedoston nimen merkkien määrän. Eli jotain muuta olet varmasti tehnyt.

Oletetaan, että olet Pythonissa avannut tiedoston, lukenut tiedoston, tulkinnut sen jotenkin avaimiksi ja arvoiksi ja sijoittanut nämä sanakirjaolioon (dict).

Pythonin sanakirja voi sisältää jokaisen avaimen vain kerran, joten jos tiedostossa on parit a–x ja a–y, sanakirjaan tulee näistä vain yksi.

sanakirja = dict()
sanakirja["a"] = "x"
sanakirja["a"] = "y"   # Tämä ylikirjoittaa edellisen.
sanakirja["b"] = "y"   # Eri avain tietenkin käy.
print(len(sanakirja))  # 2, täsmälleen oikein
print(repr(sanakirja)) # {'a': 'y', 'b': 'y'}

Jos haluat tallentaa useita arvoja sanakirjaan, sanakirjan arvona täytyy olla esimerkiksi lista tai joukko. Tällaisen sanakirjan luontia helpottaa defaultdict-luokka. Arvojen määrä täytyy kuitenkin laskea tällöin eri tavalla, koska len ilmoittaa edelleenkin vain avainten määrän.

from collections import defaultdict

sanakirja = defaultdict(list)     # Lisäystä helpottaa defaultdict.
sanakirja['a'].append('x')        # Lisätään 1.
sanakirja['a'].append('y')        # Lisätään 1.
sanakirja['b'].extend(['y', 'z']) # Lisätään 2.
sanakirja = dict(sanakirja)       # Muutetaan tavalliseksi dict-olioksi.

print(sanakirja['a'])                    # ['x', 'y']
print(len(sanakirja))                    # 2, koska on kaksi avainta.
print(sum(map(len, sanakirja.values()))) # 4, koska on yhteensä neljä arvoa.
print(repr(sanakirja))                   # {'a': ['x', 'y'], 'b': ['y', 'z']}

Kysymyksessäsi on tämä kummallinen lähtöoletus, että Python toimisi jotenkin väärin. Kuitenkin Python toimii tässä(kin) yksinkertaisessa asiassa aivan oikein, eli len antaa aivan täsmälleen dict-olion avainten lukumäärän. Jos tulos ei vastaa odotuksia, vika on jollain tavalla omassa koodissasi.

Grez [28.04.2019 13:44:41]

#

Metabolix kirjoitti:

Kysymyksessäsi on tämä kummallinen lähtöoletus, että Python toimisi jotenkin väärin.

Olen huomannut, että ihmiset ohjelman toimivan "väärin", jos se toimii eri tavalla kuin ihminen kuvittelee sen toimivan.

Jos "suomi" olisi ohjelmointikieli ja ihminen sanoisi "Kerro minulle tiedoston pituus tavuina", mutta olettaa saavansa vastauksen kysymykseen "Kerro minulle kuinka monta sanaparia tiedostossa on", niin hänestä ohjelma olisi toiminut väärin jos se antaa tiedoston pituuden tavuina.

Ja kyllähän se ohjelma tietysti väärin toimiikin, vika vaan ei ole ohjelmointikielessä vaan ohjelman kirjoittajassa.

kayttaja-14784 [28.04.2019 14:06:03]

#

Tervehdys,

kiitos kommenteista.

Avaimena toimii englannin kielen sanoja, joiden epämääräisen artikkelin käytöstä kirjoitin ohjelman.

Kaikki avaimet ovat eri sanoja, mutta eri avaimilla voi olla sama arvo 0-4, jossa 0 vastaa sanoja, joiden kanssa ei käytetä artikkelia, 1 sanoja joiden kanssa käytetään artikkelia, etc.

Sanakirjatiedosto on samassa hakemistossa lyhyen ohjelmakoodin kanssa. Kaikki sanat ja niiden avaimet ovat eri rivillä, esim.

'wear' : 0,
'uncle' : 1,
etc.

Se mitä haen, on sanaston tarkka koko ja kuinka paljon sanoja käytetään kaikissa artikkelin käytön kussakin neljässä tapauksessa.

Wordin mukaan sanoja, joiden kanssa ei käytetä epämääräistä artikkelia, on 253 kpl ja koko tiedoston sanojen määrä kaikissa tapauksissa siis silloin olisi se 1807 sanaa.

Sinänsä itse ohjelma toimii minusta kyllä oikein muuten: artikkelin käyttö tarkistetaan yksinkertaislle ehtolauseella kussakin tapauksessa erikseen kysytylle sanalle ja se näyttää vastaavan oikein.

Ilmeisesti nyt on kyse vain siitä, että len(y) laskee tiedoston koon oikein eri tavoin kuin Word ja Word:n arvio on se mitä haen tarkkaan ottaen.

Metabolix [28.04.2019 14:08:52]

#

Kirjoitit jo kolmannen viestin, ja vieläkään ei ole tullut selväksi, miten tuo data tulee tiedostosta Pythoniin. Eli ongelman ratkaisemiseksi sinun täytyy ehdottomasti näyttää se koodi, jolla lataat tiedoston Pythoniin. Onko tässä pyynnössä jotain epäselvää?

kayttaja-14784 [28.04.2019 15:05:12]

#

Tervehdys,

olen itseopiskellut Pythonia vasta tämän vuoden syksystä lähtien, joten paljon on vielä opittavaa varmaan itse peruskäsitteissäkin...

Tiedosto y on siis saman ohjelman sisällä sen ensimmäisenä osana, jossa itse ohjelmakin on. Eli siis jos nyt (mitään) ymmärrän, niin dattaa (tiedosto y) ei varsinaisesti ladata mistään Pythoniin, vaan se on osa ohjelmakoodia saman py-tiedoston sisällä kuin itse ohjelmakin. En osaa vielä tehdä tiedostokäsittelyä, joten tiedosto on samassa paikassa itse ohjelman kanssa.

Metabolix [28.04.2019 15:28:16]

#

Selvä. Luultavasti siis ongelma on nyt nimenomaan siinä, että sinulla on useita samoja sanoja. Tällöin vain viimeinen sama sana tulee mukaan.

sanakirja = {
	'take': 0,
	'take': 1,
}
# nyt sanakirja = {'take': 1}, eli ensimmäinen sana ylikirjoitetaan.
print(len(sanakirja))    # 1
print(sanakirja['take']) # 1

Ei ole kyse siitä, että ”len(y) laskee tiedoston koon oikein eri tavoin”, vaan yksinkertaisesti itse ylikirjoitat osan datastasi eli sinulla on vain 1625 eri sanaa. Jos haluat, että sinulla voisi olla monta kertaa sama sana, voit käyttää vaikka listaa:

lista = [
	('take', 0),
	('take', 1),
]
print(len(lista))  # 2
print(lista[0])    # ('take', 0)
print(lista[0][0]) # take
print(lista[0][1]) # 0

kayttaja-14784 [28.04.2019 16:24:14]

#

Tervehdys,

tästäkään ei oikeastaan pitäisi olla kyse: eli siitä, että sama sana esiintyisi useita kertoja tiedostossa.

Siinä on ensinnäksin vain substanttiveja ja kaikkien niiden pitäisi olla eri sanoja. Joitakin sanoja on, joissa kirjoitusasu poikkeaa hieman esim. s:n paikalla on c, mutta nämäkin lienevät eri sanoja. Nämä on tarkoituksella lisätty tiedostoon. Esim. license, licence.

Poimin viimeksi koko tiedoston y erilliseen python.py tiedostoon ja lisäsin sen loppuun komennon print(len(y)) ja käynnistin moduulin ja se antoi taas tulokseksi 1625 sanaa. Mitään muuta koodia tässä tiedostossa ei ollut.

Eräs asia minkä olen oppinut ohjelmoinnista on se, että samaan lopputulokseen voi päästä useilla (ei välttämättä aina optimaalisin tapa) tapaa: muutan sanaston y avainten koodausta kasvattamalla sitä yhdellä numerolla lopusta alkaen (4>5, 3>4, etc.) ja muutan ohjelmakoodin vastaavasti. Kirjoitan sitten funktion, joka käy for loopilla läpi kaikkien avainten arvot ja lisää ne muuttujaan sum, joka jaetaan sitten lopuksi avaimen arvolla:

sum1 = 0

for k, v in y.items():

if v == 2:
sum1 = sum1 + v
print (sum1//2)
continue

Tämä näyttäisi tuottavan samat arvot kuten Word-asiakirjan analyysi antoi.

Lisäys:

Tervehdys,

muistin väärin: itse asiassa aiemmin annettu funktio tuottaa ihan samat arvot, jotka vastaavat len(y):llä saatua tiedoston kokoa 1625. Minulla on se jo valmiiksi kirojoitettuna muille tapauksille paitsi avaimen arvo on 0, jolla on hankala laskea yhteen ja jakaa.

Eli lokalisoituuko ongelma, jos se nyt edes selviääkään lopulta, sitten niihin avaimen arvoihin jotenkin, kun eri sanat jakavat saman avaimen arvon.

Metabolix [28.04.2019 17:42:16]

#

Edelleenkin, jos haluat ratkaisun, lähetä koko koodi (ja sanalista) vaikka sivulle paste.dy.fi ja linkki tähän.

kayttaja-14784 [28.04.2019 18:00:03]

#

Tervehdys,

sanalistaa en halua lähettää koska se on tekijänoikeudellista materiaalia, vaikka käsittääkseni sen tekijänoikeudet on minulla, kun kokosin ja koodasin kuhunkin sananaan artikkelinkättöä koskevan avaimen arvon välillä 0-4 pari kuukautta manuaalisesti tiedostoon. Kullakin sanalla on vain yksi yksiselitteinen avaimen arvo 0-4 ja useilla sanoilla voi olla sama avaimen arvo. Pienillä testiaineistoilla testattaessa tulee oikea tulos, vaikka useilla sanoilla on sama avaimen arvo, mutta isommilla sanamäärillä tulee heittoa.

Peruskoodi tarkistaa vain ohjelmaan annetun sanan avaimen arvon ja syöttää text-ikkunaan viestin artikkelin käytöstä, esim:

x = wear> No article used.

if y[x] == 0:
     self.text.insert(END, "No article used.")
if y[x] == 1:
     self.text.insert(END, "Article is used with this word.")
etc.

Ohjelmalle on oop-tyylillä tehty tkiner-käyttöliittymä.

Kun mainitsin aiemmin, eroa näkyy tulevan eniten len(y) funktiolla ja Word:llä lasketuissa tapauksissa kun tapauksia on enemmän, esim. sanoja, joiden kanssa käytetään artikkelia on len(y):n mukaan 803, Wordin mukaan taas 926.

Jos asia ei selviä näillä tiedoilla, se voidaan jättää minun puolestani selvittämättä.

Grez [28.04.2019 18:57:05]

#

Voisit ottaa tuon koodiin sisällyttämäsi sanalistan tekstieditoriin ja katsoa montako riviä se vie. Jos siinä on yksi sana per rivi niin rivimäärän pitäisi suoraan kertoa todellisen sanojen määrän.

Oma veikkaukseni tosin on, että sanalistassa tulee joku 182 sanan pätkä kahteen kertaan ja koska Python sanakirjassa samaa sanaa ei lasketa kahteen kertaan, niin se näyttää montako sanoja oikeasti on ja Wordissa sitten että montako kertaa ne kaiken kaikkiaan on esitelty.

Metabolix [28.04.2019 21:15:33]

#

Tallenna tämä koodi nimellä sivu.html, avaa selaimessa ja laita tekstilaatikkoon sanalistasi, niin ehkä ongelma selviää.

<!DOCTYPE html>
<meta charset="UTF-8">
<title>Sanalistalaskuri</title>
<script>
function f() {
	var count = {}, list = [], all = [], multi = []
	document.querySelector("textarea").value.replace(/'(.*?)'|"(.*?)"/g, function(m, a, b) {
		var s = a || b
		all.push(s)
		if (!count[s]) {
			count[s] = 1
			list.push(s)
		} else {
			count[s] += 1
			if (count[s] == 2) {
				multi.push(s)
			}
		}
	})
	for (var i = 0; i < multi.length; ++i) {
		multi[i] = multi[i] + " (" + count[multi[i]] + ")"
	}
	document.querySelector("pre").textContent =
		"Kaikki sanat: " + all.length + "\n" +
		"Eri sanat: " + list.length + "\n" +
		"Toistuvia: " + multi.length + "\n" +
		"Toistuvat:\n\t" + multi.join("\n\t") + "\n"
}
</script>
<h1>Sanalistalaskuri</h1>
<p><textarea rows="10" cols="80">'wear' : 0,
'uncle' : 1,
</textarea></p>
<p><button onclick="f()">Laske!</button></p>
<pre></pre>

kayttaja-14784 [29.04.2019 10:58:47]

#

Tervehdys,

tarkistin sanastotiedoston ja siinä näytti sittenkin olevan tuplatermejä, joissa sama sana oli annettu kaksi kertaa. Tämä oli sitten syynä eroon len() funktion ja Wordin antamaan tulokseen kuten nimimerkki Grez arvelikin.

Tämä johtuu siitä, että samalla sanalla kuten take voi olla useita sanaluokkia kuten verbi ja substantiivi, jotka molemmat olivat alkuperäisessä sanastotiedostossa eri kohdissa eri sanaluokan sanoina ja ne molemmat tuli poimittua mukaan sanakirjatiedostoon substantiiveina.

Poistin ne nyt kaikki lajittelemalla Word:ssä sanakirjatiedoston aakkosjärjestykseen ja poistamalla tuplatermit, pientä eroa jäi tuloksiin vieläkin, mutta len() funktion tuloksia voinee pitää oikeina ja ongelmien osalta jäljet näyttivät johtavan suoraan sylttytehtaalle 😊.

Kiitoksia kaikille asiassa vaivaa nähneille, erityisti Grez ja Metabolix.


Sivun alkuun

Vastaus

Aihe on lukittu, joten siihen ei voi vastata.

Tietoa sivustosta