Вот небольшой образец более крупной строки символов, которая у меня есть (без пробелов). Он содержит вымышленные данные о лицах.
Каждый человек отделен .
Для каждого человека есть 10 атрибутов.
txt = "EREKSON(Andrew,Hélène),female10/06/2011@Geneva(Switzerland),PPF,2000X007707,dist.093,Dt.043/996.BOUKAR(Mohamed,El-Hadi),male04/12/1956@London(England),PPF,2001X005729,dist.097,Dt.043/997.HARIMA(Olak,N’nassik,Gerad,Elisa,Jeremie),female25/06/2013@Paris(France),PPF,2009X005729,dist.088,Dt.043/998.THOMAS(Hajil,Pau,Joëli),female03/03/1980@Berlin(Germany),VAT,2010X006016,dist.078,Dt.043/999."
Я хотел бы проанализировать это в фрейме данных с таким количеством наблюдений, сколько есть людей и 10 столбцов для каждой переменной.
Я пытался использовать регулярное выражение и искал другие решения для извлечения текста в stackoverflow, но не смог достичь желаемого результата.
Это последний фрейм данных, который я имею в виду, на основе ввода строки символов -
result = data.frame(first_names = c('Hélène Andrew','Mohamed El-Hadi','Olak N’nassik Gerad Elisa Jeremie','Joëli Pau Hajil'),
family_name = c('EREKSON','BOUKAR','HARIMA','THOMAS'),
gender = c('male','male','female','female'),
birthday = c('10/06/2011','04/12/1956','25/06/2013','03/03/1980'),
birth_city = c('Geneva','London','Paris','Berlin'),
birth_country = c('Switzerland','England','France','Germany'),
acc_type = c('PPF','PPF','PPF','VAT'),
acc_num = c('2000X007707','2001X005729','2009X005729','2010X006016'),
district = c('dist.093','dist.097','dist.088','dist.078'),
code = c('Dt.043/996','Dt.043/997','Dt.043/998','Dt.043/999'))
Любая помощь приветствуется
Вроде не правильно разделил
Ну, это только начало: вам нужно еще немного поработать над этим.
Вот решение, использующее tidyverse, которое объединяет различные stringr
функции для очистки строки, прежде чем readr
прочитать ее, в основном как CSV:
library(dplyr, warn.conflicts = FALSE) # for pipes
df <-
txt %>%
# Replace "." sep with newline
stringr::str_replace_all(
"\\.[A-Z]",
function(x) stringr::str_replace(x, "\\.", "\n")
) %>%
# Replace all commas in (First[,Middle1,Middle2,...]) with space
stringr::str_replace_all(
# Match anything inside brackets, but as few times as possible, so we don't
# match multiple brackets
"\\(.*?\\)",
# Inside the regex that was matched, replace comma with space
function(x) stringr::str_replace_all(x, ",", " ")
) %>%
# Replace ( with ,
stringr::str_replace_all("\\(", ",") %>%
# Remove )
stringr::str_remove_all("\\)") %>%
# Replace @ with ,
stringr::str_replace_all("@", ",") %>%
# Remove the last "."
stringr::str_replace_all("\\.$", "\n") %>%
# Add , after female/male
stringr::str_replace_all("male", "male,") %>%
# Read as comma delimited file (works since string contains \n)
readr::read_delim(
file = .,
delim = ",",
col_names = FALSE,
show_col_types = FALSE
)
# Add names (could also be done directly in read_delim with col_names argument)
names(df) <- c(
"family_name",
"first_names",
"gender",
"birthday",
"birth_city",
"birth_country",
"acc_type",
"acc_num",
"district",
"code"
)
df
#> # A tibble: 4 × 10
#> family_name first_names gender birthday birth_city birth_country acc_type
#> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 EREKSON Andrew Hélène female 10/06/2… Geneva Switzerland PPF
#> 2 BOUKAR Mohamed El-Hadi male 04/12/1… London England PPF
#> 3 HARIMA Olak N’nassik G… female 25/06/2… Paris France PPF
#> 4 THOMAS Hajil Pau Joëli female 03/03/1… Berlin Germany VAT
#> # … with 3 more variables: acc_num <chr>, district <chr>, code <chr>
Created on 2022-03-20 by the reprex package (v2.0.1)
Обратите внимание, что, вероятно, существуют более эффективные регулярные выражения, которые можно было бы использовать, но я считаю, что это проще и легче изменить позже.
Благодарю вас! Я заметил, что разделение имен некорректно, когда имена содержат специальные символы, такие как 'è' , ''' , '-' , 'è' , 'ï' , ''' и т. д.
Я немного изменил ввод «txt», чтобы рассмотреть случай, когда у человека есть 3 имени (в некоторых случаях даже 4 имени существуют в исходном фрейме данных). Как код адаптируется для учета нескольких имен?
Не так уж много нужно было бы изменить, только (1) регулярное выражение, которое находит имя/отчество внутри круглых скобок от принятия только английских букв до принятия всех слов, и (2) str_replace()
с str_replace_all()
, чтобы разрешить несколько отчеств. Я обновил свой ответ кодом, который работает с вашими новыми данными.
Это выглядит великолепно, теперь несколько имен со специальными символами, кажется, работают хорошо! Я заметил всего несколько случаев, когда это все еще не работает. Я обновил вопрос, чтобы отразить их. Есть идеи, почему они не работают, а остальные работают?
Да, \\([\\w,]\\)
соответствует «словным символам» и запятым внутри скобок, поэтому он не распознал дефис в «Эль-Хади», ' в «Н'насик» и так далее. Я обновил свой ответ более общим регулярным выражением \\(.*?\\)
. Это будет соответствовать всему, что находится внутри скобок, но как можно меньше раз (*?
). Это необходимо, так как без него можно было бы захватить несколько пар скобок одновременно.
Спасибо. Распознает ли это общее регулярное выражение символ à?
Да, точка .
будет соответствовать чему угодно, кроме новой строки (\n
). Поэтому, пока à находится внутри скобок, регулярное выражение распознает его. Вы можете проверить это так: stringr::str_detect("(à)", "\\(.*?\\)")
(возвращает TRUE
)
Понятно, мне нужно освежить регулярное выражение. Большое спасибо! Принятие вашего ответа.
Рад помочь! Регулярные выражения могут сбивать с толку. Я нахожу эту документацию по R полезной: ?stringi::about_search_regex
, и я часто использую веб-сайт это для тестирования регулярных выражений. Эти ресурсы могут оказаться полезными и для вас.
Отмеченный! Спасибо еще раз
Вот аккуратное решение с функциями tidyr
separate_rows
и extract
:
library(tidyr)
data.frame(txt) %>%
# separate `txt` into rows using the dot `.` *if*
# preceded by `Dt\\.\\d{3}/\\d{3}` as splitting pattern:
separate_rows(txt, sep = "(?<=Dt\\.\\d{3}/\\d{3})\\.(?!$)") %>%
extract(
# select column from which to extract:
txt,
# define column names into which to extract:
into = c("family_name","first_names","gender",
"birthday","birth_city","birth_country",
"acc_type","acc_num","district","code"),
# describe the string exhaustively using capturing groups
# `(...)` to delimit what's to be extracted:
regex = "([A-Z]+)\\(([\\w,]+)\\),([a-z]+)([\\d/]+)@(\\w+)\\((\\w+)\\),([A-Z]+),(\\w+),dist.(\\d+),Dt\\.([\\d/]+)")
# A tibble: 4 × 10
family_name first_names gender birthday birth_city birth_country acc_type acc_num
<chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 EREKSON Andrew,Peter male 10/06/2011 Geneva Switzerland PPF 2000X007…
2 OBAMA Barack,Hussian male 04/12/1956 London England PPF 2001X005…
3 CLINTON Hillary female 25/06/2013 Paris France PPF 2009X005…
4 GATES Melinda female 03/03/1980 Berlin Germany VAT 2010X006…
# … with 2 more variables: district <chr>, code <chr>
любой человек, который помогает любому другому человеку с регулярным выражением, заслуживает аплодисментов! Поздравляю ~ Крис
Я думаю, вы можете пойти от следующего:
library(tidyverse) txt %>% str_split("(?<=\\d)\\.(?=[A-Z])") %>% enframe %>% unnest(everything()) %>% mutate(value = str_split(value, "\\),")) %>% unnest_wider(value)
...