Проблема эффективности R: расщепление длинного символа - PullRequest
4 голосов
/ 19 марта 2019

Проблема заключается в эффективном разборе данных этого формата:

lineup = " C James McCann P Robbie Ray P Rafael Montero OF Giancarlo Stanton 3B Derek Dietrich SS Miguel Rojas 1B Tommy Joseph OF Marcell Ozuna 2B C?sar Hern?ndez OF Christian Yelich"

в массив данных из двух столбцов; один для позиции, а другой для игрока.

Имена являются игроками в бейсбол, и каждому имени предшествует их позиция, которая является точным набором {C, P, P, OF, 3B, SS, 1B, OF, 2B, OF} в некотором порядке. То есть такие точные позиции всегда происходят.

Например, «C James McCann» должен превратиться в

data.frame(position = "C", player = "James McCann")

На самом деле у меня много сотен тысяч таких строк, и я хочу их эффективно проанализировать. Вот мое неэффективное решение:

data.frame(
    position = str_match_all(lineup, "\\s[0-9A-Z]{1,2}\\s")[[1]] %>% as.character() %>% str_trim(),
    player = str_split(lineup, "\\s[0-9A-Z]{1,2}\\s")[[1]][-1],
    stringsAsFactors = F
)

Это решение по принципу Tidyverse простое, но я подозреваю, что могу сделать намного лучше. У кого-нибудь есть идеи?

Ответы [ 3 ]

3 голосов
/ 19 марта 2019

Вы могли бы создать один шаблон, который бы получал как позицию, так и имя игрока с помощью stringi :: stri_match_all_regex:

stri_match_all_regex(lineup, 
                   patt= "(C|P|OF|3B|SS|1B|OF|2B) ([A-Z][A-Za-z]+ [A-Z][A-Za-z]+)" )
[[1]]
      [,1]                   [,2] [,3]               
 [1,] "C James McCann"       "C"  "James McCann"     
 [2,] "P Robbie Ray"         "P"  "Robbie Ray"       
 [3,] "P Rafael Montero"     "P"  "Rafael Montero"   
 [4,] "OF Giancarlo Stanton" "OF" "Giancarlo Stanton"
 [5,] "3B Derek Dietrich"    "3B" "Derek Dietrich"   
 [6,] "SS Miguel Rojas"      "SS" "Miguel Rojas"     
 [7,] "1B Tommy Joseph"      "1B" "Tommy Joseph"     
 [8,] "OF Marcell Ozuna"     "OF" "Marcell Ozuna"    
 [9,] "OF Christian Yelich"  "OF" "Christian Yelich" 

Я сделал шаблон более ограничительным, чем ваш, так как мой ограничивает один илидве буквы между пробелами только для комбинаций, соответствующих позициям бейсбола.Вы получите список с элементами, которые являются матрицами для каждой строки.Вероятно, вам следует опубликовать более сложный пример для поддержки дальнейшей обработки, которая потребуется.Вам нужно будет использовать что-то вроде lapply( results, function(x){ as.data.frame(x[ , -1]) })

lapply( results, function(x){ as.data.frame(x[ , -1]) })
[[1]]
  V1                V2
1  C      James McCann
2  P        Robbie Ray
3  P    Rafael Montero
4 OF Giancarlo Stanton
5 3B    Derek Dietrich
6 SS      Miguel Rojas
7 1B      Tommy Joseph
8 OF     Marcell Ozuna
9 OF  Christian Yelich

Если будут дефисные имена, отчества или инициалы, шаблон может быть более сложным.

2 голосов
/ 19 марта 2019

Вот решение, которое преобразует lineup в строку в формате файла csv, который затем читается как fread():

library(magrittr)  # piping used to improve readability
lineup %>% 
  stringr::str_replace_all("\\s(C|P|OF|SS|1B|2B|3B)\\s", "\\\n\\1;") %>% 
  data.table::fread(header = FALSE, col.names = c("position", "player"))
    position            player
 1:        C      James McCann
 2:        P        Robbie Ray
 3:        P    Rafael Montero
 4:       OF Giancarlo Stanton
 5:       3B    Derek Dietrich
 6:       SS      Miguel Rojas
 7:       1B      Tommy Joseph
 8:       OF     Marcell Ozuna
 9:       2B   C?sar Hern?ndez
10:       OF  Christian Yelich

«Трюк»должен поставить разрыв строки перед символами позиции и разделителем столбцов после, например, " C " становится "\nC;".

lineup %>% 
  stringr::str_replace_all("\\s(C|P|OF|SS|1B|2B|3B)\\s", "\\\n\\1;")

возвращает

[1] "\nC;James McCann\nP;Robbie Ray\nP;Rafael Montero\nOF;Giancarlo  Stanton\n3B;Derek Dietrich\nSS;Miguel Rojas\n1B;Tommy Joseph\nOF;Marcell Ozuna\n2B;C?sar Hern?ndez\nOF;Christian Yelich"

Этот подход не делает много предположений об именах.Он даже работает с именами, такими как James P. McCann или Robbie Ray, Jr.

lineup2 %>% 
  stringr::str_replace_all("\\s(C|P|OF|SS|1B|2B|3B)\\s", "\\\n\\1;") %>% 
  data.table::fread(header = FALSE, col.names = c("position", "player"))
    position            player
 1:        C   James P. McCann
 2:        P    Robbie Ray, Jr
 3:        P  Rafael D Montero
 4:       OF Giancarlo Stanton
 5:       3B    Derek Dietrich
 6:       SS      Miguel Rojas
 7:       1B      Tommy Joseph
 8:       OF     Marcell Ozuna
 9:       2B   C?sar Hern?ndez
10:       OF  Christian Yelich

Существует три предварительных условия, которые должны быть выполнены:

  1. Именная часть не должна содержать инициалы, которые также используются в качестве позицииИндикаторы, например, инициалы C и P, должны заканчиваться точкой, чтобы избежать путаницы.
  2. Разделитель столбцов ; не должен использоваться в других местах в lineup.
  3. Строка должна начинаться с начального пробела.

Условие 3 можно изменить с помощью улучшенного регулярного выражения, а условие 2 можно проверить на:

lineup3 %T>% 
  {stopifnot(!stringr::str_detect(., ";"))} %>% 
  stringr::str_replace_all("(^\\s?|\\s)(C|P|OF|SS|1B|2B|3B)\\s", "\\\n\\2;") %>% 
  data.table::fread(header = FALSE, col.names = c("position", "player"))
    position            player
 1:        C   James P. McCann
 2:        P    Robbie Ray, Jr
 3:        P    Rafael Montero
 4:       OF Giancarlo Stanton
 5:       3B    Derek Dietrich
 6:       SS      Miguel Rojas
 7:       1B      Tommy Joseph
 8:       OF     Marcell Ozuna
 9:       2B   C?sar Hern?ndez
10:       OF  Christian Yelich

Данные

# original
lineup = " C James McCann P Robbie Ray P Rafael Montero OF Giancarlo Stanton 3B Derek Dietrich SS Miguel Rojas 1B Tommy Joseph OF Marcell Ozuna 2B C?sar Hern?ndez OF Christian Yelich"

# other use cases
lineup1 = "C James McCann P Robbie Ray P Rafael Montero OF Giancarlo Stanton 3B Derek Dietrich SS Miguel Rojas 1B Tommy Joseph OF Marcell Ozuna 2B C?sar Hern?ndez OF Christian Yelich"
lineup2 = " C James P. McCann P Robbie Ray, Jr P Rafael D Montero OF Giancarlo Stanton 3B Derek Dietrich SS Miguel Rojas 1B Tommy Joseph OF Marcell Ozuna 2B C?sar Hern?ndez OF Christian Yelich"
lineup2a = " C James P. McCann P Robbie Ray P Rafael Montero OF Giancarlo Stanton 3B Derek Dietrich SS Miguel Rojas 1B Tommy Joseph OF Marcell Ozuna 2B C?sar Hern?ndez OF Christian Yelich"
lineup2b = " C James McCann P Robbie Ray, Jr P Rafael Montero OF Giancarlo Stanton 3B Derek Dietrich SS Miguel Rojas 1B Tommy Joseph OF Marcell Ozuna 2B C?sar Hern?ndez OF Christian Yelich"
lineup3 = "C James P. McCann P Robbie Ray, Jr P Rafael Montero OF Giancarlo Stanton 3B Derek Dietrich SS Miguel Rojas 1B Tommy Joseph OF Marcell Ozuna 2B C?sar Hern?ndez OF Christian Yelich"
lineup4 = " C James P. McCann P Robbie Ray; Jr P Rafael Montero OF Giancarlo Stanton 3B Derek Dietrich SS Miguel Rojas 1B Tommy Joseph OF Marcell Ozuna 2B C?sar Hern?ndez OF Christian Yelich"
2 голосов
/ 19 марта 2019

Вот вариант stringr::str_split, использующий позитивный прогноз и прогноз

pos <- c("C", "P", "P", "OF", "3B", "SS", "1B", "OF", "2B", "OF")
pat <- sprintf("(%s)", paste(pos, collapse = "|"))

library(stringr)
matrix(unlist(str_split(trimws(lineup), sprintf(
    "((?<=(%s))\\s|\\s(?=(%s)))", pat, pat))), ncol = 2, byrow = T)
#    [,1] [,2]
#[1,] "C"  "James McCann"
#[2,] "P"  "Robbie Ray"
#[3,] "P"  "Rafael Montero"
#[4,] "OF" "Giancarlo Stanton"
#[5,] "3B" "Derek Dietrich"
#[6,] "SS" "Miguel Rojas"
#[7,] "1B" "Tommy Joseph"
#[8,] "OF" "Marcell Ozuna"
#[9,] "2B" "C?sar Hern?ndez"
#[10,] "OF" "Christian Yelich"

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...