Доступ к n-му элементу после разбиения строки - PullRequest
0 голосов
/ 05 февраля 2019

У меня есть строка, которая выглядит следующим образом:

string <- c("A,1,some text,200", "B,2,some other text,300", "A,3,yet another one,100")

Таким образом, каждый элемент вектора далее делится запятыми.Теперь я хочу извлечь элементы только в определенном месте.Скажем, все элементы до первой запятой или все элементы после второй запятой.

Следующий код делает то, что я хочу:

sapply(strsplit(string, ","), function(x){return(x[[1]])})
# [1] "A" "B" "A"
sapply(strsplit(string, ","), function(x){return(x[[3]])})
# [1] "some text" "some other text" "yet another one"

Однако этот код кажется мне довольно сложным (учитывая простоту вопроса).Есть ли более лаконичные варианты для достижения того, чего я хочу?

Ответы [ 5 ]

0 голосов
/ 05 февраля 2019

Это можно сделать с помощью базы R, используя regepr:

regmatches(string, regexpr("^[^,]", string))
[1] "A" "B" "A"
regmatches(string, regexpr("[^,]*$", string))
[1] "200" "300" "100"
regmatches(string, regexpr("[^,]*,[^,]*$", string))
[1] "some text,200"       "some other text,300" "yet another one,100"
0 голосов
/ 05 февраля 2019

1) data.frame Преобразовать в фрейм данных, а затем легко выделить столбец или подмножество столбцов:

DF <- read.table(text = string, sep = ",", as.is = TRUE)

DF[[1]]
## [1] "A" "B" "A"

DF[[3]]
## [1] "some text"       "some other text" "yet another one"

DF[-1]
##   V2              V3  V4
## 1  1       some text 200
## 2  2 some other text 300
## 3  3 yet another one 100

DF[2:3]
##   V2              V3
## 1  1       some text
## 2  2 some other text
## 3  3 yet another one

2) данных.table :: tranpose В пакете data.table есть функция для транспонирования списков, так что если stringt - это транспонированный список, то stringt[[3]] - вектор третьих полей, скажем, аналогично (1).Еще более компактным является data.table tstrsplit, упомянутый @Henrik ниже или тот же пакет fread, упомянутый @akrun ниже.

library(data.table)

stringt <- transpose(strsplit(string, ","))

# or
stringt <- tstrsplit(string, ",")

stringt[[1]]
## [1] "A" "B" "A"

stringt[[3]]
## [1] "some text"       "some other text" "yet another one"

stringt[-1]
## [[1]]
## [1] "1" "2" "3"
##
## [[2]]
## [1] "some text"       "some other text" "yet another one"
##
## [[3]]
## [1] "200" "300" "100"

stringt[2:3]
## [[1]]
## [1] "1" "2" "3"
##
## [[2]]
## [1] "some text"       "some other text" "yet another one"

purrr также имеет функцию transpose, но

library(purrr)
transpose(strsplit(string, ","))

создает список списков, а не список символьных векторов.

0 голосов
/ 05 февраля 2019

Мы можем упростить код OP до:

sapply(strsplit(string, ","), '[', 1)
# [1] "A" "B" "A"

sapply(strsplit(string, ","), '[', 3)
# [1] "some text"       "some other text" "yet another one"

Кроме того, с помощью stringr::str_split и simplify = TRUE мы можем напрямую индексировать столбец, поскольку на выходе будет матрица:

library(stringr)
str_split(string, ",", simplify = TRUE)[,1]
# [1] "A" "B" "A"

str_split(string, ",", simplify = TRUE)[,3]
# [1] "some text"       "some other text" "yet another one"
0 голосов
/ 05 февраля 2019

Небольшое отклонение от версии с sapply():

sapply(strsplit(string, ","), function(x) x[1])

[1] "A" "B" "A"

sapply(strsplit(string, ","), function(x) x[3])

[1] "some text"       "some other text" "yet another one"

. Может быть и другая возможность: tstrsplit из data.table:

tstrsplit(string, ",")[[1]]

[1] "A" "B" "A"

.различные решения:

library(microbenchmark)
microbenchmark(
 tmfmnk_sapply = sapply(strsplit(string, ","), function(x) x[1]),
 tmfmnk_tstrsplit = tstrsplit(string, ",")[[1]],
 avid_useR_sapply = sapply(strsplit(string, ","), '[', 1),
 avid_useR_str_split = str_split(string, ",", simplify = TRUE)[,1],
 Ronak_Shah = word(string, 1, sep = ","),
 times = 5
)

                expr     min      lq     mean  median      uq     max neval cld
       tmfmnk_sapply  34.543  36.395  45.8782  47.150  48.540  62.763     5  a 
    tmfmnk_tstrsplit  33.072  33.554  39.1166  35.012  36.116  57.829     5  a 
    avid_useR_sapply  39.612  45.292  61.1936  46.730  47.398 126.936     5  a 
 avid_useR_str_split  27.313  34.095  49.3412  43.834  43.977  97.487     5  a 
          Ronak_Shah 146.875 147.277 199.4978 162.995 218.322 322.020     5   b

Тесты на реплицированной «строке»:

string <- rep(string, 1e5)

microbenchmark(
 tmfmnk_sapply = sapply(strsplit(string, ","), function(x) x[1]),
 tmfmnk_tstrsplit = tstrsplit(string, ",")[[1]],
 avid_useR_sapply = sapply(strsplit(string, ","), '[', 1),
 avid_useR_str_split = str_split(string, ",", simplify = TRUE)[,1],
 Ronak_Shah = word(string, 1, sep = ","),
 Christoph = regmatches(string, regexpr("^[^,]", string)),
 times = 5
)

                expr       min        lq      mean    median        uq       max neval
       tmfmnk_sapply 1529.8955 1608.2909 1926.7776 1820.0443 2105.9736 2569.6836     5
    tmfmnk_tstrsplit 1277.8712 1281.0371 1482.4520 1314.0074 1599.7686 1939.5757     5
    avid_useR_sapply 1428.7175 1470.9002 1487.5425 1483.1127 1521.3735 1533.6087     5
 avid_useR_str_split  306.2633  316.7539  360.8785  334.8516  335.5375  510.9863     5
          Ronak_Shah 5541.6199 5657.3593 5955.9653 6068.1067 6166.7249 6346.0157     5
           Christoph  231.0496  244.1049  383.9702  246.0421  273.2877  925.3667     5
0 голосов
/ 05 февраля 2019

Одним из вариантов является использование word из stringr с аргументом sep

library(stringr)
word(string, 1, sep = ",")
#[1] "A" "B" "A"

word(string, 3, sep = ",")
#[1] "some text"       "some other text" "yet another one"

Поскольку производительность word является худшей среди всех, я обнаружил другой вариант, использующийрегулярное выражение в базе R.

#Get 1st element
sub("(?:[^,],){0}([^,]*).*", "\\1",string)
#[1] "A" "B" "A"

#Get 3rd element
sub("(?:[^,],){2}([^,]*).*", "\\1",string)
#[1] "some text"       "some other text" "yet another one"

Здесь нужно сопоставить две группы.Первый соответствует любым символам, которые не являются запятой, за которой следует запятая n раз, а затем снова соответствует другому набору символов, которые не являются запятыми.Первая группа не захвачена (?:), а вторая группа захвачена и возвращена.Также обратите внимание, что число в скобках ({}) должно быть на единицу меньше нужного нам слова.Таким образом, {0} возвращает 1-е слово, а {2} возвращает 3-е слово.

Тест

string <- c("A,1,some text,200","B,2,some other text,300","A,3,yet another one,100")
string <- rep(string, 1e5)

library(microbenchmark)
microbenchmark(
  tmfmnk_sapply = sapply(strsplit(string, ","), function(x) x[1]),
  tmfmnk_tstrsplit = tstrsplit(string, ",")[[1]],
  avid_useR_sapply = sapply(strsplit(string, ","), '[', 1),
  avid_useR_str_split = str_split(string, ",", simplify = TRUE)[,1],
  Ronak_Shah_word = word(string, 1, sep = ","),
  Ronak_Shah_sub = sub("(?:[^,],){0}([^,]*).*", "\\1",string),
  G_Grothendieck ={DF <- read.table(text = string, sep = ",",as.is = TRUE);DF[[1]]},
  times = 5
)
#Unit: milliseconds
#               expr     min      lq    mean  median      uq     max neval
#      tmfmnk_sapply 1629.69 1641.61 2128.14 1834.99 1893.43 3640.96     5
#   tmfmnk_tstrsplit 1269.94 1283.79 1286.29 1286.68 1290.76 1300.30     5
#   avid_useR_sapply 1445.40 1447.64 1555.76 1498.14 1609.52 1778.13     5
#avid_useR_str_split  324.68  332.28  332.30  333.97  334.01  336.54     5
#    Ronak_Shah_word 6571.29 6810.92 6956.20 6930.86 7217.26 7250.69     5
#     Ronak_Shah_sub  349.76  354.77  356.91  358.91  359.17  361.94     5
#     G_Grothendieck  354.93  358.24  364.43  362.24  367.79  378.94     5

Я не включил решение Кристофа, так как не ясномне, как это будет работать для переменных n.Например, для 3-й позиции, для 4-й позиции и т. Д.

...