Эффективный разбор строки из столбца в таблицу данных в R

У меня есть таблица данных с полем для подсчета людей из каждой страны в формате «XYZ: #», где XYZ — это код страны, а # количество.

Вот пример 4 строк из таблицы данных:

dt <-data.table(event = c("Event 1", "Event 2", "Event 3", "Event 4"),
       desc = c("Desc1", "Desc1", "Desc2", "Desc3"),
       countries = c("USA:433, MEX:132, GRC:58, GBR:50, IRL:35, ITA:20",
                     "ESP:42, DEU:40, ITA:20, SLE:7",
                     "GBR:78, JAM:63, USA:30, AUT:18, GHA:5",
                     "NLD:53, GBR:21, CHN:20"))

Поле «страны» может содержать любое количество записей, единственная структура которых состоит в том, что оно начинается от наиболее многочисленных к наименее многочисленным. Мне нужно разобрать это в таблицу данных для анализа комбинаций событий и описаний. Поэтому мне нужна таблица, показывающая количество по коду страны, со строками в том же порядке, чтобы я мог объединить ее обратно с полями события и описания.

Мое решение до сих пор очень неэффективно, оно перебирает каждую строку, а затем анализирует столбец стран сначала для разделителя запятой, а затем для двоеточия. Вот что у меня есть до сих пор:

# Reference data for ISO 3-alpha country codes as character vector
nat_codes <- read.csv("countries_codes.csv")[[3]]

# Create empty data table

master_dt <- data.table(matrix(nrow = 0, ncol = length(nat_codes)+1))
names(master_dt) <- c(sort(nat_codes), "Unknown")

for (i in 1:nrow(dt)){
    
    # split each string by the "," separator
    row_component <- strsplit(dt$countries, ",")[[i]]
    
    country_codes <- c()
    numbers <- c()
    
    # Loop through each component and separate country from count
    for (j in 1:length(row_component)){
        
        my_split <- strsplit(row_component[[j]], ":")[[1]]
        
        # Extract the country code, add to vector
        country_codes[j] <- gsub("\\s+", "", my_split[1])
        
        # Extract the number
        numbers[j] <- as.numeric(my_split[2])
    }
    # Re-assemble country codes and numbers as a data table and bind
    

    row_dt <- data.table(country = country_codes, number = numbers)
    row_dt <- transpose(row_dt)
    names(row_dt) <- as.character(row_dt[1,])
    row_dt <- row_dt[-1,]
    
    master_dt <- rbindlist(list(master_dt, row_dt),
                           fill = TRUE,
                           use.names = TRUE)
    
}
# remove rows with no entries
master_dt <- Filter(function(x)!all(is.na(x)), master_dt)

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

    USA  MEX  GRC  GBR  IRL  ITA  ESP  DEU  SLE  JAM  AUT  GHA  NLD  CHN
1:  433  132   58   50   35   20 <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>
2: <NA> <NA> <NA> <NA> <NA>   20   42   40    7 <NA> <NA> <NA> <NA> <NA>
3:   30 <NA> <NA>   78 <NA> <NA> <NA> <NA> <NA>   63   18    5 <NA> <NA>
4: <NA> <NA> <NA>   21 <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>   53   20

Мне нужно использовать это для сотен тысяч записей, но это довольно медленно для тестового набора около 1000 строк. Каковы самые простые победы здесь для повышения производительности? Предпочтительны базовые и data.table решения.

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

Не могли бы вы также опубликовать ожидаемый результат?

Andre Wildberg 15.04.2023 17:04

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

stackmeistergeneral 15.04.2023 17:14
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
66
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Здесь я использовал другой пакет reshape2, чтобы помочь с трансформацией.

Петля все еще есть, но значительно короче. Используя dt, как указано в вашем коде, я сделал следующее.

dt_expand <- data.table()

for(i in 1:nrow(dt)){

  row_component <- unlist(strsplit(dt[i, countries], ","))
  
  TempDT <- data.table(event = rep(dt[i, event], length(row_component)),
                       countries = gsub(":.*", "", row_component),
                       counts = gsub(".*:", "", row_component))
  
  dt_expand <- rbind(dt_expand, TempDT)
  
}

dt_recast <- dcast(dt_expand, event ~ countries, value.var = "counts")

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

library(dplyr)

dt %>% 
  mutate(countries = strsplit(countries, ", ")) %>% 
  unnest(countries) %>% 
  separate(countries, into = c("countries", "number"), sep = ":")

Хорошо, я думаю, что вам нужен tyr для отдельной(), а не только dplyr (если только моя версия не устарела). Обычно я избегаю tidyverse, но +1 за читабельность.

stackmeistergeneral 16.04.2023 13:11
Ответ принят как подходящий

Использование sapply и списка unique счетчика всех стран.

library(data.table)

countr <- unique(sub(":.*", "", unlist(strsplit(dt$countries, ", "))))

data.table(t(sapply(strsplit(dt$countries, ", "), function(co) 
  sapply(countr, function(x){
    res <- sub(".*:", "", grep(x, co, value=T))
    ifelse(identical(res, character(0)), NA, res)}))))
    USA  MEX  GRC  GBR  IRL  ITA  ESP  DEU  SLE  JAM  AUT  GHA  NLD  CHN
1:  433  132   58   50   35   20 <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>
2: <NA> <NA> <NA> <NA> <NA>   20   42   40    7 <NA> <NA> <NA> <NA> <NA>
3:   30 <NA> <NA>   78 <NA> <NA> <NA> <NA> <NA>   63   18    5 <NA> <NA>
4: <NA> <NA> <NA>   21 <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>   53   20

Для сравнения скорости немного измененная версия, работающая в индексированном data.table и использующая lapply

countr <- unique(sub(":.*", "", unlist(strsplit(dt$countries, ", "))))

setNames(data.table(t(dt[, lapply(strsplit(countries, ", "), function(co) 
  lapply(countr, function(x){
    res <- sub(".*:", "", grep(x, co, value=T))
    ifelse(identical(res, character(0)), NA, res)}))])), countr)
   USA MEX GRC GBR IRL ITA ESP DEU SLE JAM AUT GHA NLD CHN
1: 433 132  58  50  35  20  NA  NA  NA  NA  NA  NA  NA  NA
2:  NA  NA  NA  NA  NA  20  42  40   7  NA  NA  NA  NA  NA
3:  30  NA  NA  78  NA  NA  NA  NA  NA  63  18   5  NA  NA
4:  NA  NA  NA  21  NA  NA  NA  NA  NA  NA  NA  NA  53  20

Спасибо, это здорово. И решает проблему отсутствия кодов стран в справочном списке. Я бы подумал, что работа с индексированной таблицей данных будет быстрее, но microbenchmark считает, что первая быстрее, это ожидается?

stackmeistergeneral 16.04.2023 13:08

@stackmeistergeneral Интересно, я бы предположил, что второй тоже быстрее, но, возможно, поскольку он не работает в пределах исходной структуры данных, перестановка делает его медленнее.

Andre Wildberg 16.04.2023 14:29

Другие вопросы по теме