2016-07-27 8 views
0

Ich habe einen Zeichenvektor von Börsentickern, bei denen der Tickername mit dem Land, in dem dieser Ticker basiert, in der folgenden Form verkettet ist: country_name/ticker_name. Ich versuche, jede Zeichenfolge zu teilen und alles von der '/' zurück zu löschen, einen Zeichenvektor nur der Tickernamen zurückgeben. Hier ist ein Beispielvektor:Löschen von Zeichen vor regulärem Ausdruck (R)

sample_string <- c('US/SPY', 'US/AOL', 'US/MTC', 'US/PHA', 'US/PZI', 
        'US/AOL', 'US/BRCM') 

Mein erster Gedanke wäre, die Stringr-Bibliothek zu verwenden. Ich habe nicht wirklich keine Erfahrung mit diesem Paket, aber hier ist das, was ich habe versucht:

library(stringr) 
split_string <- str_split(sample_string, '/') 

Aber ich war nicht sicher, wie nur das zweite Element jeder Liste als einziger Vektor zurückzukehren.

Wie würde ich dies über einen großen Zeichenvektor (~ 105 Millionen Einträge) tun?

+2

Es gibt viele Möglichkeiten, dies zu erreichen. Zum Beispiel 'sub (". */(. *) "," \\ 1 ", sample_string)' ODER 'sub (". */"," ", Sample_string)' oder vielleicht wird das schneller, weil Vermeiden von Regex 'data.table :: tstrsplit (Beispiel_String,"/", fest = TRUE) [[2]]' –

+0

Sind die '. */(. *)' hier als Zeiger? –

+0

Es bedeutet "* match alles vor dem Back-Slash (einschließlich) und erfassen alles danach *". Dann sagt '\\ 1'' sub', um die erfasste Gruppe zurückzugeben.Obwohl ich denke, dass es in diesem Fall überkompleziert ist. Die anderen beiden Optionen sind wahrscheinlich besser/einfacher. Wie auch immer, diese Art von Frage wurde viele Male auf SO gestellt, Sie sollten Google wirklich ausprobieren, wenn Sie mehr Regex-Beispiele in R sehen möchten. Und ich empfehle diese Seite für zukünftige Regex-Tests https://regex101.com/ und dieses für Tutorials http://www.regular-expressions.info/tutorial.html –

Antwort

3

Einige Benchmark hier einschließlich aller von @David Arenburg vorgeschlagen, und eine andere Methode mit str_extract von stringr Paket.

sample_string <- rep(sample_string, 1000000) 

library(data.table); library(stringr) 
s1 <- function() sub(".*/(.*)", "\\1", sample_string) 
s2 <- function() sub(".*/", "", sample_string) 
s3 <- function() str_extract(sample_string, "(?<=/)(.*)") 
s4 <- function() tstrsplit(sample_string, "/", fixed = TRUE)[[2]] 

length(sample_string) 
# [1] 7000000 

identical(s1(), s2()) 
# [1] TRUE 
identical(s1(), s3()) 
# [1] TRUE 
identical(s1(), s4()) 
# [1] TRUE 

microbenchmark::microbenchmark(s1(), s2(), s3(), s4(), times = 5) 
# Unit: seconds 
# expr  min  lq  mean median  uq  max neval 
# s1() 3.916555 3.917370 4.046708 3.923246 3.925184 4.551184  5 
# s2() 3.584694 3.593755 3.726922 3.610284 3.646449 4.199426  5 
# s3() 3.051398 3.062237 3.354410 3.138080 3.722347 3.797985  5 
# s4() 1.908283 1.964223 2.349522 2.117521 2.760612 2.996971  5 

Die tstrsplit Methode ist die schnellste.

aktualisieren:

eine andere Methode von @Frank hinzufügen, dieser Vergleich nicht ganz richtig ist, die auf den tatsächlichen Daten abhängt, wenn es eine Menge von duplizierten Fällen ist wie die sample_string oben hergestellt wird, ist der Vorteil ganz offensichtlich:

s5 <- function() setDT(list(sample_string))[, v := tstrsplit(V1, "/", fixed = TRUE)[[2]], by=V1]$v 

identical(s1(), s5()) 
# [1] TRUE 

microbenchmark::microbenchmark(s1(), s2(), s3(), s4(), s5(), times = 5) 
# Unit: milliseconds 
# expr  min  lq  mean median  uq  max neval 
# s1() 3905.97703 3913.264 3922.8540 3913.4035 3932.2680 3949.3575  5 
# s2() 3568.63504 3576.755 3713.7230 3660.5570 3740.8252 4021.8426  5 
# s3() 3029.66877 3032.898 3061.0584 3052.6937 3086.9714 3103.0604  5 
# s4() 1322.42430 1679.475 1985.5440 1801.9054 1857.8056 3266.1101  5 
# s5() 82.71379 101.899 177.8306 121.6682 209.0579 373.8141  5 
+1

Länge der Probe ist nett, aber warum nicht einfach den Code zeigen, um es zu machen? – Frank

+1

@Frank Ich wiederholte den sample_string von OP um 1 Million mal. Zu der Antwort hinzugefügt. – Psidom

+1

Ok. Die große Anzahl der wiederholten Werte missbrauchen ... 's5 <- function() setDT (liste (sample_string)) [, v: = tstrsplit (V1,"/", fest = WAHR) [[2]], by = V1 ] $ v' Könnte auch für das OP relevant sein, da sie AOL zweimal haben. Es testet als identisch mit s4 und 10x-ish schneller. – Frank

2

Einige hilfreiche Hinweise zu Ihrer Frage: zum einen gibt es eine str_split_fixed Funktion im stringr Paket, das tut, was Sie es lapply telefonisch tun wollen.

library(data.table); library(stringr) 
sample_string <- c('US/SPY', 'US/AOL', 'US/MTC', 'US/PHA', 'US/PZI', 
        'US/AOL', 'US/BRCM') 
sample_string <- rep(sample_string, 1e5) 
split_string <- str_split_fixed(sample_string, '/', 2)[,2] 

Es funktioniert durch stringi::stri_split_fixed Aufruf und ist nicht unähnlich

do.call("c", lapply(str_split(sample_string, '/'),"[[",2)) 

Zweitens, eine andere Art und Weise jedes zweite Element der Liste zu denken Extrahieren von genau das tun, was tstrsplit intern tut.

transpose(strsplit(sample_string, "/", fixed = T))[[2]] 

Auf insgesamt Seite beachten, sollte die oben marginal schneller als tstrsplit aufrufen. Dies ist natürlich nicht wert, ausführlich zu tippen, aber es hilft zu wissen, was die Funktion tut.

library(data.table); library(stringr) 
s4 <- function() tstrsplit(sample_string, "/", fixed = TRUE)[[2]] 
s5 <- function() transpose(strsplit(sample_string, "/", fixed = T))[[2]] 

identical(s4(), s5()) 
microbenchmark::microbenchmark(s4(), s5(), times = 20) 

microbenchmark::microbenchmark(s4(), s5(), times = 20) 
Unit: milliseconds 
expr  min  lq  mean median  uq  max neval 
s4() 161.0744 193.3611 255.8136 234.9945 271.6811 434.7992 20 
s5() 140.8569 176.5600 233.3570 194.1676 251.7921 420.3431 20 

dieses zweiten Verfahrens betrifft, so kurz, diese Liste der Länge 7.000.000 Umsetzung, die jeweils mit 2 Elementen wird Ihr Ergebnis auf eine Liste der Länge 2, jeweils mit 7 Millionen Elemente umwandeln. Sie extrahieren dann das zweite Element dieser Liste.

+0

Hinweis: 'transpose 'ist auch eine Funktion im data.table-Paket, also könnte es genauso gut strsplit, oder? Transponiert sich auch selbst in C geschrieben zu sein. – Frank

+0

@Frank, ja du hast Recht. Ich habe meine Antwort bearbeitet, um das zu reflektieren. Worauf ist der Unterschied zurückzuführen? Funktionsüberhang? oder ist es möglich, dass die C-Funktion von R schneller transponiert als die C-Funktion von data.table. Ich denke, ich werde später etwas erforschen müssen. – shayaa

+0

Hm, der Unterschied ist klein; Ich würde mir keine Sorgen machen. Wahrscheinlich über Ihnen, wie Sie vermutet haben. Ich würde nur den Wrapper (tstrsplit) verwenden, anstatt mich für ein paar zusätzliche Millisekunden in seine Interna zu vertiefen. Genauso würde ich auch nicht mit '.subset2' oder' .Internal (mean (x)) 'von der Basis gammeln diese bieten viel größere Leistungsverbesserungen http://stackoverflow.com/a/17010701/ und http://stackoverflow.com/questions/14209427/improve-performance-function#comment19719256_14213913 – Frank

Verwandte Themen