Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Python: saman lukujonon sisältävien rivien tuonti

Sivun loppuun

rica [08.02.2013 19:51:22]

#

On kaksi eri .txt tiedostoa ja pitää saada toisesta txt:stä vain saman lukujonon sisältävät rivit kuin toisen txt tiedostossa olevat. Voiko tälläisen koodin tehdä?

Eli esim txt A on tälläinen:
7968
7969
8023
8050

ja txt B:
32823978 X 4480 100
32823978 D 8023
32823978 E 6399 600
32823978 C 5760 230
32823978 E 7968 600
32823978 C 8050 230

Ja koodin ajettua txt B:stä vain saman luvun sisältävät jäljellä:
32823978 D 8023
32823978 E 7968 600
32823978 C 8050 230

Ja molemmat tiedostot sisältävät yli milj. riviä, eli ei voi ainakaan kirjottaa koodin jokaista txt A:n lukujonoa..

L2-K2 [08.02.2013 19:56:52]

#

Jos et kerran "voi kirjoittaa tiedostoa A koodiin", niin miten olisi jos luet sen ensin vaikka taulukkoon, tai vielä parempi luet sen luvut ja merkkaat ne sanakirjaan (dictionary). Sitten vain luet tiedoston B läpi verraten löytyykö kyseistä lukua sanakirjasta.

Metabolix [08.02.2013 20:11:33]

#

#!/usr/bin/python3
import re
luvut = dict((int(s.strip()), True) for s in open("A.txt", "r"))
open("C.txt", "w").writelines(s for s in open("B.txt", "r") if int(re.split("\\s+", s)[2]) in luvut)

Chiman [08.02.2013 23:07:58]

#

Yllä onkin jo valmis ratkaisu, mutta tässä pari lisävinkkiä: dictin sijaan voi käyttää joukkoa (set), jolloin vältytään tarpeettomilta arvoilta. Lisäksi Pythonin regexp käännetään yllä jokaisella re.splitin suorituskerralla, mikä voi jo vähän tuntua yli miljoonassa rivissä. Vaihtoehtoisesti merkkijonon voi kääntää regexp-objektiksi re.compilella vain suorituksen alussa ja ajaa sitten splitit valmiilla regexpillä.

import re
re_whitespaces = re.compile('\\s+')
# ...
# silmukassa sitten: ... int(re_whitespaces.split(s)[2]) ...

Chiman [10.02.2013 11:55:29]

#

Testasinpa vielä huvikseni erilaisten toteutusten nopeuseroja. Dictin korvaaminen setillä ei vaikuta nopeuteen.
Versio A on Metabolixin koodi yllä.
Versio B:ssä regexp käännetään vain kerran (ja otetaan set käyttöön):

#!/usr/bin/python3
import re
re_whitespaces = re.compile('\\s+')
luvut = set(int(s.strip()) for s in open("A.txt", "r"))
open("C.txt", "w").writelines(s for s in open("B.txt", "r") if int(re_whitespaces.split(s)[2]) in luvut)

Versio C:ssä kirjoitetaan kaikki kerralla:

#!/usr/bin/python3
import re
re_whitespaces = re.compile('\\s+')
luvut = set(int(s.strip()) for s in open("A.txt", "r"))
out = ''.join(s for s in open("B.txt", "r") if int(re_whitespaces.split(s)[2]) in luvut)
open("C.txt", "w").write(out)

Versio D:ssä C:hen verrattuna jätetään int-muunnokset tekemättä:

#!/usr/bin/python3
import re
re_whitespaces = re.compile('\\s+')
luvut = set(s.strip() for s in open("A.txt", "r"))
out = ''.join(s for s in open("B.txt", "r") if re_whitespaces.split(s)[2] in luvut)
open("C.txt", "w").write(out)

Versio E:ssä D:hen verrattuna korvataan regexp-split merkkijonon split-metodilla:

#!/usr/bin/python3
luvut = set(s.strip() for s in open("A.txt", "r"))
out = ''.join(s for s in open("B.txt", "r") if s.split()[2] in luvut)
open("C.txt", "w").write(out)

Ja suoritusajat, kun A.txt ja B.txt sisältävät molemmat 2 miljoonaa riviä:
A: 18 s
B: 15 s
C: 14 s
D: 8,0 s
E: 3,1 s

Kaikki koodit tuottavat identtisen C.txt:n.

Muokkaus: poistettu ylimääräiset rivinvaihdot C-version outputista, ei nopeusmerkitystä

Metabolix [10.02.2013 13:47:15]

#

Hienoa optimointia.

rica [10.02.2013 15:55:00]

#

Kiitos avusta!

Jos koitan tuota E vaihtoehtoa, niin ajanko vain koodin ja pidän samassa kansiossa A ja B tekstin?

Ja tarkoitus on, että B teksistä ei häviä rivien muut luvut ja numerot, vaan C txt:iin tulee kaikki rivit kokonaisuudessaan, jotka sisältävät saman lukujonon kun A:ssa on

Lisäys:

Tuo Metabolixin koodi onnistui, ongelma oli vaan että C tekstiin tuli myös rivit jossa osa A:n luvuista

Eli jos A:ssa oli:
1817226
162400
4211630
4252812

Niin C:hen tuli myös nämä:
28801151 D 17226
28801686 D 2400
28801686 D 21163
28801785 D 2812

Pitäisi saada vain ne, jossa on luku täsmälleen. Pystyykö toteuttaa?

Metabolix [10.02.2013 17:44:26]

#

Nyt olet kyllä itse tunaroinut jotain, koodini (ja myös mikä tahansa Chimanin versio) toimii täysin oikein. Oletko tarkistanut, ettei A.txt:ssä ole noiden rivien lukuja?

rica [10.02.2013 18:50:46]

#

Eikun joo sori, katsoin väärää tekstiä. Kiitti tuli täysin oikein!

rica [01.10.2014 19:30:44]

#

Osaisko joku auttaa ajan tuon:

import re
luvut = dict((int(s.strip()), True) for s in open("A.txt", "r"))
open("C.txt", "w").writelines(s for s in open("B.txt", "r") if int(re.split("\\s+", s)[2]) in luvut)

mutta A.txt:

57744
29258
110786
110786
112984
84169

ja B.txt:

1107562	273100
1107563	278000
1107671	270000
110786	275800

Niin tulee ValueError: invalid literal for int () with base 10 "

Mod. lisäsi kooditagit!

Metabolix [01.10.2014 19:38:46]

#

Sinulta puuttuu tiedostosta B.txt toinen sarake. Jos tiedoston muoto muuttuu, sinun pitää tietenkin vastaavasti muuttaa koodissa tutkittavaa saraketta (tuossa numero 2, sarakkeet alkavat nollasta).


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta