Maciej B.

Maciej B. Doktorant

Temat: Wczytywanie plików fixed width format

Witam,

chciałem Was podpytać czy istnieje możliwość efektywnego (tj. szybkiego) wczytania dużych plików tekstowych, które nie mają separowanych kolumn (fixed width format)?

Chciałem ostatnio wczytać w miarę duży (100k obserwacji i 90 zm.) zbiór danych do R, do łatwiejszego określenia zakresu zmiennych wykorzystałem kody SAS i pakiet SAScii, następnie poleceniem read.fwf chciałem wczytać owy zbiór danych i...trwało to wieki czyli 2 minuty, gdzie SAS wczytał zbiór w 2 sekundy. W przypadku zbioru który ma ok. 500k obs. i 90 zm wczytywanie trwało baaaardzo długo (prawie 60 min).

Czy istnieje możliwość przyspieszenia tego procederu?

poniżej kod i zbiór, który mnie interesuje:

Zbiór: http://pisa2009.acer.edu.au/downloads/INT_PAR09_DEC11.zip
Kod SAS: http://pisa2009.acer.edu.au/downloads/INT_PAR09_SAS_DE...



library(SAScii)
a<-Sys.time()

x<-parse.SAScii('INT_PAR09_SAS_DEC11.sas')
parents<-read.fwf('INT_PAR09_DEC11.txt',widths=x$width)
names(parents)<-x$varname
Sys.time()-a



R na którym pracuję i komputer:

R version 2.15.1 (2012-06-22)
Platform: x86_64-pc-mingw32/x64 (64-bit)

locale:
[1] LC_COLLATE=Polish_Poland.1250 LC_CTYPE=Polish_Poland.1250 LC_MONETARY=Polish_Poland.1250
[4] LC_NUMERIC=C LC_TIME=Polish_Poland.1250

attached base packages:
[1] stats graphics grDevices utils datasets methods base

other attached packages:
[1] SAScii_0.3 XLConnect_0.1-9 XLConnectJars_0.1-4 rJava_0.9-3

loaded via a namespace (and not attached):
[1] tools_2.15.1


Procesor: i7-2630QM 2GHz, 8GB DDR3.
Michał Bojanowski

Michał Bojanowski socjolog, analityk

Temat: Wczytywanie plików fixed width format

czytanie plików fwf przez R jest niestety wyjątkowo mało efektywne (w porównaniu do CSV, tab-delimited itp.)

Spróbuj (w kolejności od mniej do bardziej hakerskich rozwiązań):

0. Skoro masz dostęp do SASa: wczytaj dane SASem i zapisz jako np. CSV, ktore zaladujesz do R read.csv

1. Użyć argumentu colClasses w read.fwf do określenia typu zmiennych (numeric, character, etc.) w wynikowym data framie. Powinno znacznie skrócić czas (nawet o 40% w przypadku większych plików).

2. W zależności od ilości RAMu w maszynie, użyć większej wartości dla argumentu buffersize do read.fwf

3. Wrzucić dane do bazy (SQLite, MySQL, ...) i wczytywać dane do R z bazy a nie z pliku.

4. Podzielić plik z danymi na 'k-1' mniejszych plików (gdzie k jest liczbą rdzeni w twoim procesorze) a następnie wczytać je read.fwf jak zaproponowałeś, ale równolegle (np. mclapply) z jednej instancji R, a potem "skleić".

5. używając innego narzędzia (bash, sed, perl, .....) zamienić format pliku na CSV (albo coś-delimited). read.table jest duuużo szybsze niż read.fwf. też warto zastosować (1) powyżej do read.table.
Maciej B.

Maciej B. Doktorant

Temat: Wczytywanie plików fixed width format

Szkoda...myślałem, że uda się bez kombinowania bo już z csv (i SAS) próbowałem.

Zwiększanie buffersize nie pomogło, praktycznie nie było różnicy między 1000 a 10000.
Spróbuję z argumentem colClasses, mam nadzieję, że przyspieszy :)
Wojciech Sobala

Wojciech Sobala Redaktor
statystyczny,
biostatystyk,
Instytut Medycyny
Pr...

Temat: Wczytywanie plików fixed width format

Pierwszy krok - wczytanie danych za pomocą readLines,
Drugi krok - podział danych na kolumny.
Przetestowałem na pliku który udostępniłeś.
Oto wyniki:

[quote]fn <- dir(pattern="txt")[/quote]> system.time(dane <- readLines(fn))
użytkownik system upłynęło
3.11 0.00 2.82


> system.time(dane_mat <- do.call(rbind,
+ mapply(FUN=substring,dane,
+ MoreArgs=list(first=def_txt_start,
+ last=def_txt_end),
+ SIMPLIFY=FALSE,
+ USE.NAMES=FALSE)))
użytkownik system upłynęło
4.31 0.03 3.94


Tyle samo wczytanie do macierzy z podziałem na kolumny.
W programie SASa który dołączyłeś jest jeszcze kilka innych operacji ale te na pewno nie powinny trwać wiele minut.
def_txt_start i def_txt_end skopiowałem z programu SAS, który załączyłeś (oczywiście nie występują tam pod takimi nazwami).Wojciech Sobala edytował(a) ten post dnia 24.07.12 o godzinie 21:07
Maciej B.

Maciej B. Doktorant

Temat: Wczytywanie plików fixed width format

Super! Wielkie dzięki za to rozwiązanie! Podaję wyniki wczytywania pliku INT_PAR09_DEC11.txt na moim komputerze:


system.time(dane <- readLines(fn[1]))
user system elapsed
1.03 0.01 1.05



system.time(dane_mat <- do.call(rbind,mapply(FUN=substring,dane,MoreArgs=list(first=x$V3,last=x$V4),SIMPLIFY=FALSE,USE.NAMES=FALSE)))
user system elapsed
8.19 0.02 8.21
Maciej B. edytował(a) ten post dnia 25.07.12 o godzinie 08:12
Michał Bojanowski

Michał Bojanowski socjolog, analityk

Temat: Wczytywanie plików fixed width format

Wojciech Sobala:
Pierwszy krok - wczytanie danych za pomocą readLines,
Drugi krok - podział danych na kolumny.
Przetestowałem na pliku który udostępniłeś.


Nieźle... Nie spodziewałem się, wczytanie tego w ten sposób będzie aż tak szybkie... read.fwf jest do kitu.
Maciej B.

Maciej B. Doktorant

Temat: Wczytywanie plików fixed width format

Próbowałem wczytywać większy plik (INT_STQ09_DEC11.txt, ~515k obs., 437 zm., 920mb), który jest udostępniony na stronie PISA, no i R już sobie nie dał z tym rady z tym kodem. Zapełnił praktycznie cały dostępny ram i zawiesił komputer...W formacie CSV nie było problemu.

linki do danych i kodów:
http://pisa2009.acer.edu.au/downloads/INT_STQ09_DEC11.zip
http://pisa2009.acer.edu.au/downloads/INT_STQ09_SAS_DE...

edit. pewnie trzeba się pobawić w podzielenie pliku ale w to później się pobawię. ;)Maciej B. edytował(a) ten post dnia 25.07.12 o godzinie 14:28
Michał Bojanowski

Michał Bojanowski socjolog, analityk

Temat: Wczytywanie plików fixed width format

Maciej B.:
Próbowałem wczytywać większy plik (INT_STQ09_DEC11.txt, ~515k obs., 437 zm., 920mb), który jest udostępniony na stronie PISA, no i R już sobie nie dał z tym rady z tym kodem. Zapełnił praktycznie cały dostępny ram i zawiesił komputer...

Może dlatego, że procesując je w wojciechowy sposób tworzysz dwie kopie danych: wektor wierszy oraz wynikową macierz. Tak procesowane obiekty raczej nie współdzielą pamięci. Sufit dla wykorzystywania pamięci przez R na Windowsach (też 64bit) może być niżej niż się wydaje... help("Memory-limits")

Ja bym chyba wrzucał takie dane do bazy. I tak pewnie w żadnym momencie analiz nie będziesz korzystał ze wszystkich 437-iu zmiennych jednocześnie.

EDIT:
Jeżeli zdecydujesz się na bazę MySQL, to kiedyś były problemy z instalacją RMySQL pod windowsami i wtedy popełniłem tą notatkę:
http://www.bojanorama.pl/public:r:rmysql
Może to już nieaktualne, nie jestem pewien bo rozstałem się z Windowsami już jakiś czas temu.Michał Bojanowski edytował(a) ten post dnia 26.07.12 o godzinie 02:57
Wojciech Sobala

Wojciech Sobala Redaktor
statystyczny,
biostatystyk,
Instytut Medycyny
Pr...

Temat: Wczytywanie plików fixed width format

Z moich testów dotyczących wielkości zbioru wynika, że do około 100 tys. rekordów z wczytywaniem zbioru do R nie powinno być większych problemów (nawet wtedy gdy jest 1000 zmiennych).
Przy wczytywaniu zbiorów większych niż 1 mln rekordów można być prawie pewnym, że będą problemy.
To w którym dokładnie miejscu (mam tu na myśli liczbę rekordów) pojawią się problemy z konkretnym zbiorem trudno przewidzieć.
Podałem moje rozwiązanie ze względu na to, że mniejszy ze zbiorów zdecydowanie kwalifikował się do pierwszej kategorii i wydawało mi się, że czas wczytywania rzędu godzin można napewno poprawić.
Wczytywanie plików w formacie csv jest zdecydowanie lepiej rozwiązane (wczytanie za pomocą readLines i następnie użycie strsplit będzie wolniejsze niż read.table).
Rozwiązanie podane przez @Michała skaluje się do plików nawet z milionami rekordów. Wczytywanie danych z pliku tekstowego do bazy danych jest naprawdę szybkie (nawet 10 razy szybsze niż przy użyciu readLines). Transfer danych z bazy danych do R nie zawsze jest szybki i zależy od tego czy łączymy się przez ODBC czy w bezpośredni sposób.
Ja osobiście zaczął bym od bazy SQLite chyba, że na komputerze jest i tak zainstalowany MySQL.
Wojciech Sobala

Wojciech Sobala Redaktor
statystyczny,
biostatystyk,
Instytut Medycyny
Pr...

Temat: Wczytywanie plików fixed width format

Aby zaoszczędzić nieco pamięci podczas wczytywania danych można to zrobić w pętli for dodając do funkcji readLines parametr n (plik w tym przypadku należy otworzyć przy pomocy funkcji file).
Sprawdziłem kilka kombinacji ilość linii - liczba iteracji i wygląda na to, że czas wykonania jest zbliżony dla kombinacji rzędu 1000-100 i 50000-2.
Funkcją która powoduje podwojenie zapotrzebowania na pamięć jest rbind, więc należałoby najpierw utworzyć docelową strukturę danych (tutaj dane_mat) i partiami ją wypełniać danymi.
W skrajnym przypadku można obniżyć zużycie pamięci niemalże o połowę nie zwiększając czasu wykonania więcej niż o około 30%.
Maciej B.

Maciej B. Doktorant

Temat: Wczytywanie plików fixed width format

Bardzo dziękuję za wszelkie rady, będę dalej działał.
Tomasz Stokowy

Tomasz Stokowy Doktorant,
Politechnika Śląska

Temat: Wczytywanie plików fixed width format

Najlepszym rozwiązaniem do wczytywania dużych danych w R jest polecenie read.table. Niestety, zarządzanie pamięcią w R jest jedną z jego podstawowych wad, dlatego wielkość plików które da się wczytać zależy od ilości naszego RAM. Przy 4GB RAM da się wczytać pliki ~2GB, przy dobrze skonfigurowanym systemie (Win7 64bit). Przy tak dużych danych R często nie daje sobie rady nawet z najprostszymi pętlami. Dobrym rozwiązaniem, a czasami jedynym wyjściem staje się wtedy Perl lub C.
Wojciech Sobala

Wojciech Sobala Redaktor
statystyczny,
biostatystyk,
Instytut Medycyny
Pr...

Temat: Wczytywanie plików fixed width format

Spróbowałem wczytać plik 6 razy większy (orginał + 5 kopii). Oto wyniki:

[quote]system.time(dane <- readLines(fn[3]))[/quote]użytkownik     system   upłynęło 
45.09 0.28 45.10
> [quote]gc()[/quote] used (Mb) gc trigger (Mb) max used (Mb)
Ncells 339062 9.1 597831 16.0 340482 9.1
Vcells 2981411 22.8 4119321 31.5 3751967 28.7[quote]
system.time(dane_mat <- do.call(rbind,[/quote]+ mapply(FUN=substring,dane,
+ MoreArgs=list(first=def_txt_start,
+ last=def_txt_end),
+ SIMPLIFY=FALSE,
+ USE.NAMES=FALSE)))
użytkownik system upłynęło
66.60 0.43 66.54
> [quote]gc()[/quote] used (Mb) gc trigger (Mb) max used (Mb)
Ncells 372972 10.0 4193053 112.0 4838477 129.3
Vcells 31748061 242.3 64047767 488.7 60765211 463.7


Komputer na którym testowałem ma 2GB (Vista 32 bit, R 2.15.1).
Napisałem też krótki program w Pythonie i czas odczytu + podział na pola łącznie wyniósł około 9s.
Można zatem spróbować Python + rpy2 aby dane wczytać do R. Z drugiej strony tragedii nie ma bo wszystko trwa niecałe 2 minuty.

Następna dyskusja:

Wczytywanie danych z pliku ...




Wyślij zaproszenie do