2012-06-20 15 views
5

Mam plik danych tekstowych, który prawdopodobnie będę czytać z readLines. Początkowa część każdego łańcucha zawiera dużo bełkotania, po którym znajdują się dane, których potrzebuję. Brzęczenie i dane są zwykle oddzielone trzema kropkami. Chciałbym podzielić struny po ostatnich trzech kropkach, lub zamienić ostatnie trzy kropki na marker oznaczający R, aby traktować wszystko po lewej stronie tych trzech kropek jako jedną kolumnę.R: usuwanie ostatnich trzech kropek z ciągu

Oto podobny post Stackoverflow że zlokalizuje ostatnią kropkę:

R: Find the last dot in a string

Jednak w moim przypadku niektóre dane mają dziesiętne, więc lokalizowanie ostatnią kropkę nie wystarczy. Ponadto, myślę, że ... ma specjalne znaczenie w R, co może komplikować problem. Kolejną potencjalną komplikacją jest to, że niektóre kropki są większe niż inne. W niektórych liniach jedna z trzech kropek została zastąpiona przecinkiem.

Poza postacią gregexpr w powyższym wpisie próbowałem użyć gsub, ale nie mogę znaleźć rozwiązania.

Oto dawać przykład dane, a wynik mam nadzieję osiągnąć:

aa = matrix(c(
'first string of junk... 0.2 0 1', 
'next string ........2 0 2', 
'%%%... ! 1959 ... 0 3 3', 
'year .. 2 .,. 7 6 5', 
'this_string is . not fine .•. 4 2 3'), 
nrow=5, byrow=TRUE, 
dimnames = list(NULL, c("C1"))) 

aa <- as.data.frame(aa, stringsAsFactors=F) 
aa 

# desired result 
#        C1 C2 C3 C4 
# 1  first string of junk 0.2 0 1 
# 2   next string ..... 2 0 2 
# 3    %%%... ! 1959  0 3 3 
# 4     year .. 2  7 6 5 
# 5 this_string is . not fine 4 2 3 

Mam nadzieję, że kwestia ta nie jest uważana za zbyt szczegółowe. Plik danych tekstowych został utworzony za pomocą kroków opisanych w moim poście z wczoraj na temat odczytu pliku MSWord w R.

Niektóre linie nie zawierają bełkotu lub trzech kropek, ale tylko dane. Może to jednak być komplikacją dla postu uzupełniającego.

Dziękuję za radę.

+0

Czy możesz wyszukać i zamienić najpierw przecinki i duże kropki na zwykłe kropki? –

+0

Myślę, że nie mogę zastąpić przecinków kropkami, ponieważ dane zawierają przecinki w większej liczbie: 4500. Zapomniałem o tym wspomnieć w moim poście. Chociaż może mógłbym zastąpić przecinki kropkami, a następnie usunąć kropki z danych po tym, jak wyeliminowałem bełkot. –

Odpowiedz

5

To działa, chociaż nie jest szczególnie eleganckie ...

options(stringsAsFactors = FALSE) 


# Search for three consecutive characters of your delimiters, then pull out 
# all of the characters after that 
# (in parentheses, represented in replace by \\1) 
nums <- as.vector(gsub(aa$C1, pattern = "^.*[.,•]{3}\\s*(.*)", replace = "\\1")) 

# Use strsplit to break the results apart at spaces and just get the numbers 
# Use unlist to conver that into a bare vector of numbers 
# Use matrix(, nrow = length(x)) to convert it back into a 
# matrix of appropriate length 
num.mat <- do.call(rbind, strsplit(nums, split = " ")) 


# Mash it back together with your original strings 
result <- as.data.frame(cbind(aa, num.mat)) 

# Give it informative names 
names(result) <- c("original.string", "num1", "num2", "num3") 
+0

Warto zauważyć, że "wielka kropka" sprawiała mi kłopoty, gdy próbowałem wysłać ten kod z Vima - jednak po skopiowaniu ze strony działa to dobrze. Tak więc mój przepływ był do edycji w Vimie, wklejeniu na stronę, a następnie wklejeniu do mojej konsoli ... to nie jest w porządku. –

+0

Wygląda na to, że kod przypisuje liczby 4,2,3 (od ostatniego ciągu) do wszystkich 5 łańcuchów w zbiorze danych. –

+0

@MarkMiller Ah, przepraszam - pracowałem z macierzą 'aa', a nie jako data.frame. Jeśli chcesz użyć data.frame, możesz po prostu przypisać 'nums' w ten sposób:' as.vector (gsub (aa $ C1, wzorzec = "^. * [., •] {3} \\ s * (. *) ", replace =" \\ 1 "))' –

0

Odwrotny ciąg
Odwrotny wzór szukasz jeśli to konieczne - to nie jest w twoim przypadku
Odwrotny wynik

[haiku-pseudokod]

a = 'first string of junk... 0.2 0 1' // string to search 
b = 'junk' // pattern to match 

ra = reverseString(a) // now equals '1 0 2.0 ...knuj fo gnirts tsrif' 
rb = reverseString (b) // now equals 'knuj' 

// run your regular expression search/replace - search in 'ra' for 'rb' 
// put the result in rResult 
// and then unreverse the result 
// apologies for not knowing the syntax for 'R' regex 

[/ haiku-pseudocode]

+1

Czy to haiku? Aw, nieważne ... Nadal nie rozumiem. – GSee

+0

Zobacz edycję haiku – KevinDTimm

2

To będzie Ci większość drogi tam, i będzie miał żadnych problemów z numerami, które zawierają przecinki:

# First, use a regex to eliminate the bad pattern. This regex 
# eliminates any three-character combination of periods, commas, 
# and big dots (•), so long as the combination is followed by 
# 0-2 spaces and then a digit. 
aa.sub <- as.matrix(
    apply(aa, 1, function (x) 
    gsub('[•.,]{3}(\\s{0,2}\\d)', '\\1', x, perl = TRUE))) 

# Second: it looks as though you want your data split into columns. 
# So this regex splits on spaces that are (a) preceded by a letter, 
# digit, or space, and (b) followed by a digit. The result is a 
# list, each element of which is a list containing the parts of 
# one of the strings in aa. 
aa.list <- apply(aa.sub, 1, function (x) 
    strsplit(x, '(?<=[\\w\\d\\s])\\s(?=\\d)', perl = TRUE)) 

# Remove the second element in aa. There is no space before the 
# first data column in this string. As a result, strsplit() split 
# it into three columns, not 4. That in turn throws off the code 
# below. 
aa.list <- aa.list[-2] 

# Make the data frame. 
aa.list <- lapply(aa.list, unlist) # convert list of lists to list of vectors 
aa.df <- data.frame(aa.list)  
aa.df <- data.frame(t(aa.df), row.names = NULL, stringsAsFactors = FALSE) 

Jedyne co pozostało, aby zmodyfikować regex dla strsplit() tak, że może obsłużyć drugi ciąg w aa. A może lepiej poradzić sobie z takimi przypadkami ręcznie.

+0

Jeśli dodaję spację między ostatnią kropką a 2 w drugim ciągu, możesz zmodyfikować linię aa.list zajmij się tym? W moich prawdziwych danych uważam, że zawsze była spacja po ostatniej kropce i po prostu nie zdawałem sobie z tego sprawy, kiedy stworzyłem "aa". Mogę również spróbować dowiedzieć się, jak zmodyfikować aa.list. –

+0

Tak, jeśli dodasz spację między ostatnią kropką a drugą w drugim łańcuchu, można zmodyfikować wyrażenie regularne w drugim kroku, aby obsłużyć ten ciąg. To trochę trudne, ale wykonalne. To powiedziawszy, myślę, że @MattParker ma lepszy pomysł: zacznij od rozdzielenia każdego z twoich napisów na "złą" część (pierwsza kolumna) i dobrze zachowaną część (kolumny danych). Następnie zastosuj wyrażenia regularne do pierwszej kolumny. Następnie połącz ponownie dwie części. Jeśli zrobisz to w ten sposób, możesz zachować zwykłe wyrażenie w 'strsplit' całkiem proste. W przeciwnym razie wyrażenie regularne będzie bardziej złożone. – user697473

Powiązane problemy