Условное разделение строки кадра данных на основе второго пробела

У меня есть кадр данных, который я хочу разделить текстовую строку первого столбца на два столбца, но только после второго пробела в последовательности. Вот пример:

                           test22 Ticker
1        Current SharePrice $6.57    MFM
2               Current NAV $7.11    MFM
3 Current Premium/Discount -7.59%    MFM
4        52WkAvg SharePrice $6.55    MFM
5               52WkAvg NAV $7.21    MFM
6 52WkAvg Premium/Discount -9.19%    MFM

По сути, если конечным результатом будет фрейм данных с тремя столбцами, а поле цена /% будет отдельным столбцом. Спасибо!


person js80    schedule 15.01.2019    source источник


Ответы (3)


Вот вариант с использованием dplyr и stringr:

library(dplyr)
library(stringr)

data <-
  tibble(test22 = c("Current SharePrice $6.57",
                    "Current NAV $7.11",
                    "Current Premium/Discount -7.59%",
                    "52WkAvg SharePrice $6.55",
                    "52WkAvg NAV $7.21",
                    "52WkAvg Premium/Discount -9.19%"),
         Ticker = "MFM")

data %>% 
  mutate(category = str_replace(test22, "^(.+ .+) (.+)$", "\\1"),
         price_pc = str_replace(test22, "^(.+ .+) (.+)$", "\\2"))


# A tibble: 6 x 4
test22                          Ticker category                 price_pc
<chr>                           <chr>  <chr>                    <chr>   
1 Current SharePrice $6.57        MFM    Current SharePrice       $6.57   
2 Current NAV $7.11               MFM    Current NAV              $7.11   
3 Current Premium/Discount -7.59% MFM    Current Premium/Discount -7.59%  
4 52WkAvg SharePrice $6.55        MFM    52WkAvg SharePrice       $6.55   
5 52WkAvg NAV $7.21               MFM    52WkAvg NAV              $7.21   
6 52WkAvg Premium/Discount -9.19% MFM    52WkAvg Premium/Discount -9.19% 

EDIT: объяснение используемого регулярного выражения

Игнорируя скобки на секунду:

^ = начало строки

. = любой символ, кроме новой строки

+ = хотя бы один из предыдущих символов (в данном случае любой символ, кроме новой строки)

$ = конец строки

Таким образом, "^(.+ .+) (.+)$" ищет строки, которые начинаются, содержат несколько символов, затем пробел, затем несколько символов, затем пробел, затем еще несколько символов, а затем конец.

Скобки добавляются как «группы захвата», что означает, что запрос «запоминает» часть строки, которая представлена ​​этими скобками, и может быть извлечена путем ссылки на порядок скобок. Следовательно, "\\1" возвращает то, что было захвачено первой скобкой, а "\\2" возвращает то, что было захвачено второй.

Хорошим ресурсом для изучения Regex является Regexr.

person g_t_m    schedule 15.01.2019
comment
Не беспокойтесь @js80. Мы будем очень признательны за некоторые ответы/выбранные ответы. - person g_t_m; 15.01.2019
comment
Не могли бы вы объяснить синтаксис: ^(.+ .+) (.+)$, \\1? - person js80; 15.01.2019
comment
@js80 добавил объяснение выше, так как оно было слишком многословным для комментария. Вкратце: это Regex. - person g_t_m; 16.01.2019

Одним из вариантов в базе r является создание разделителя , с sub, а затем используйте read.csv:

out <- cbind(read.csv(text = sub(" (\\S+)$", ",\\1", df1$test22), 
       header = FALSE, stringsAsFactors = FALSE), df1[2])
out
#.                       V1     V2 Ticker
#1       Current SharePrice  $6.57    MFM
#2              Current NAV  $7.11    MFM
#3 Current Premium/Discount -7.59%    MFM
#4       52WkAvg SharePrice  $6.55    MFM
#5              52WkAvg NAV  $7.21    MFM
#6 52WkAvg Premium/Discount -9.19%    MFM

Или используя extract из tidyr

library(tidyverse)
df1 %>% 
     extract(test22, into = c("V1", "V2"), "^(\\S+\\s+\\S+)\\s+(.*)")
#                        V1     V2 Ticker
#1       Current SharePrice  $6.57    MFM
#2              Current NAV  $7.11    MFM
#3 Current Premium/Discount -7.59%    MFM
#4       52WkAvg SharePrice  $6.55    MFM
#5              52WkAvg NAV  $7.21    MFM
#6 52WkAvg Premium/Discount -9.19%    MFM

данные

df1 <- structure(list(test22 = c("Current SharePrice $6.57", "Current NAV $7.11", 
  "Current Premium/Discount -7.59%", "52WkAvg SharePrice $6.55", 
 "52WkAvg NAV $7.21", "52WkAvg Premium/Discount -9.19%"), Ticker = c("MFM", 
 "MFM", "MFM", "MFM", "MFM", "MFM")), class = "data.frame", row.names = c("1", 
  "2", "3", "4", "5", "6"))
person akrun    schedule 15.01.2019

Вот вариант с использованием strsplit

data.frame(do.call(rbind, strsplit(df$test22, '\\s(?!.*\\s)', perl = TRUE)), 
           Ticker=df$Ticker)
#                         X1     X2 Ticker
# 1       Current SharePrice  $6.57    MFM
# 2              Current NAV  $7.11    MFM
# 3 Current Premium/Discount -7.59%    MFM
# 4       52WkAvg SharePrice  $6.55    MFM
# 5              52WkAvg NAV  $7.21    MFM
# 6 52WkAvg Premium/Discount -9.19%    MFM

Или с помощью gsub

gsub('.*\\s.*?\\s(.*)','\\1', df$test22, perl = TRUE)
# [1] "$6.57"  "$7.11"  "-7.59%" "$6.55"  "$7.21"  "-9.19%"
# or if factors
# gsub('.*\\s.*?\\s(.*)','\\1', as.character(df$test22), perl = TRUE)

Преимущество второго заключается в том, что он действительно учитывает второй символ пробела (в отличие от последнего пробела).

person niko    schedule 15.01.2019
comment
При этом я получаю следующую ошибку, но применяю ее к большому фрейму данных: Ошибка в strsplit(df6$test22, \\s(?!.*\\s), perl = TRUE): несимвольный аргумент - person js80; 15.01.2019
comment
@ js80 Возможно, потому что у вас есть факторы. Попробуйте: as.character(df$test22) в strsplit вместо df$test22. Или, может быть, попробовать второй вариант. - person niko; 15.01.2019