2016-02-05 18 views
6

Mam pliki danych o stałej szerokości (.dbf), które nie mają separatorów linii. Oto co dwie linie tego pliku danych wygląda następująco:Importuj plik danych o stałej szerokości bez separatora linii

20141101 77h 3.210         0 3 20141102 76h 3.090         0 3 

Szerokości jednej linii jest c(8,4,7,41) do daty (8), niektóre pomiaru czasu (4), punkt danych (7), i kilka innych kolumn, które mogę podsumować w jednej kolumnie "odpoczynku" (41). Po jednej linii nie ma separatora, a następna linia jest po prostu dołączana do pierwszej linii. Wszystkie etapy są zasadniczo pisane kolejno w jednej masywnej linii. W tym pliku znajdują się wyłącznie cyfry, znaki i biała spacja.

Z read.fwf('filepath', widths = c(8,4,7,41)) R zatrzymuje czytanie po pierwszej linii z powodu braku separatora linii.

Czy istnieje argument informujący, read.fwf(), kiedy rozpocząć czytanie nowej linii, gdy nie ma separatora linii? Czy powinienem użyć innego polecenia odczytu?

Z góry dziękuję.

+3

Możesz dodać separator linii co 60 znaków nie? ([Przykład z sed] (http://stackoverflow.com/questions/1187078/how-to-insert-a-new-line-character-after-a-fixed-number-of-characters-in-a-file)) – Tensibai

Odpowiedz

3

inny, a prawdopodobnie mniej eleganckie rozwiązanie z readLines, substr, trimws, separate (tidyr) i mutate_all (dplyr):

txt <- readLines('filepath') 
dfx <- data.frame(V1 = sapply(seq(from=1, to=nchar(txt), by=60), 
           function(x) substr(txt, x, x+59))) 

library(dplyr) 
library(tidyr) 
dfx %>% 
    separate(V1, c(paste0("V",LETTERS[1:5])), c(8,12,19,55)) %>% 
    mutate_all(trimws) 

co daje:

 VA VB VC VD VE 
1 20141101 77h 3.210 0 3 
2 20141102 76h 3.090 0 3 

Aby uzyskać różne nazwy kolumn, wystarczy zastąpić c(paste0("V",LETTERS[1:5]) z wektorem nazwy kolumn, które chcesz.

Jeśli chcesz przekształcić kolumny w poprawne klasy zamiast w character, możesz użyć funs(ul = type.convert(trimws(.))) wewnątrz mutate_all.

+0

Czyste i idealne rozwiązanie. Dzięki! – Ben

+0

@Ben Thanx :-) Dodano teraz również notatkę o poprawności klas kolumn. – Jaap

4
nie

Być może najlepszy pomysł, ale to powinno działać:

content <- scan('filepath','character',sep='~') # Warning choose a sep not appearing in datas to get the whole file. 
# Split content in lines: 
lines <- regmatches(content,gregexpr('.{60}',content))[[1]] 
x <- tempfile() 
write(lines,x) 
data <- read.fwf(x, widths = c(8,4,7,41)) 
unlink(x) 

Chodzi o to, aby przeczytać cały plik, dostać każde wystąpienie 60 znaków na jeden wpis, zapisz to do pliku tymczasowego i czytać dane z tego pliku tymczasowego przed usunięciem pliku tymczasowego.

Innym podejściem jest wykonalne z regexes i pakować stringr (wciąż o zawartości wynikającej ze skanowania wcześniej):

library(stringr) 
d <- data.frame(str_match_all(content, "(.{8})(.{4})(.{7})(.{41})")[[1]][,2:5], stringsAsFactors=FALSE) 

co daje:

 V1 V2  V3          V4 
1 20141101 77h 3.210         0 3 
2 20141102 76h 3.090         0 3 

str_match_all powrót na liście, tutaj z 1 elementu poznieważ jako dane wejściowe jest tylko jedna linia, więc usuwamy ją za pomocą [[1]].

Teraz zwracamy 5 kolumn, z których pierwsza jest pełna, a pozostałe są grupami przechwytywania, więc dzielimy macierz na kolumny od 2 do 5, aby uzyskać tylko 4 kolumny, których potrzebujemy i zawijamy je w as.data.frame, aby uzyskać data.frame na końcu.

można następnie wymienić kolumny z colnames(d) <- c('date','time','data_point','rest')

Jeśli chcesz oczyścić spacji można owinąć wynik str_extract_all w trimws (dzięki @jaap dla przypominać tej funkcji) jak to:

td <- data.frame(trimws(str_match_all(content, "(.{8})(.{4})(.{7})(.{41})")[[1]][,2:5]), stringsAsFactors=FALSE) 

wyjściowa:

 X1 X2 X3  X4 
1 20141101 77h 3.210 0 3 
2 20141102 76h 3.090 0 3 
+1

bardzo dziękuję za sugestię, ale jest taki sam problem z tym rozwiązaniem, że read.fwf() zatrzymuje się po pierwszym wierszu. – Ben

+0

@ Naprawdę, powinienem był to sprawdzić. Zaktualizuję odpowiedź za pomocą odpowiedniego rozwiązania. – Tensibai

+0

@ Tensibai nie zajmują dużo czasu, ponieważ odpowiedź Jaapa działa dobrze. Ale dzięki za twoją radę! – Ben

0

W uzupełnieniu do innych odpowiedzi, kilka ogólnych informacji o dbf files:

O ile nie jest to jednorazowa odczytać pliku statycznego, byłoby najlepiej, aby sprawdzić strukturę plików/boiska pierwszy wypadek, który zmienia się w ciągu czas. Zobacz here dla wewnętrznej struktury pliku dbf.

Ale może jeszcze ważniejsze:

Każdy zapis w pliku dbf poprzedza jeden bajt do kasowania flagi. Jeśli jest to spacja, rekord nie jest usuwany, jeśli jest to gwiazdka * rekord jest oznaczony do usunięcia (rekordy nie są usuwane z pliku dbf, dopóki plik nie jest zapakowany) i prawdopodobnie chcesz pominąć te rekordy. Pierwsza część danych może również zostać zastąpiona na przykład "DELETED".

Więc w rekordzie c(8,4,7,41), ostatni bajt kolumny spoczynku (41) jest rzeczywiście kasowania flagi rekordu, który po nim - i ostatni zapis w pliku będzie mieć tylko 40 bajtów dla tego pola (ale jeśli masz szczęście, plik ma znacznik EOF (0x1a), więc może nie masz problemu z rozmiarem).

W związku z tym Twój rekord powinien być: c(1,8,4,7,40), gdzie 1 jest flagą usuwania i początkiem jednego bajtu wcześniej.

+0

Bardzo przydatne informacje, dzięki! – Ben

Powiązane problemy