Отдельные элементы каждой ячейки в фрейме данных в R

#r #dataframe #data-cleaning

#r #фрейм данных #очистка данных

Вопрос:

У меня есть R dataframe, в котором каждая ячейка настроена в следующем формате: number (число, число), следующим образом

 #create dataframe as follows
x <- data.frame("Col1" = c("0.4646 (0.4061, 0.7522)", "0.4137 (0.0178, 0.617)"), "Col2"= c("0.1996 (0.1383, 0.3499)", "0.9814 (0.7092, 0.9884)"), stringsAsFactors = FALSE)

x[1,1]
#"0.4646 (0.4061, 0.7522)"

#I want 0.4646 to go in a column called "Col1est"
#0.4061 should go in a column called "Col1lower"
#0.7522 should go in a column called "Col1Upper"


  

Для каждой записи x я хочу разделить ее на 3 столбца. Запрос представлен в коде. Как мне извлечь конкретные значения из круглых скобок и поместить их в новые столбцы?

~ Генеральный директор

Ответ №1:

Вы можете попробовать этот подход, изменив данные на long и используя separate_rows() для разделения значений. Здесь код:

 library(tidyverse)
#Code
newx <- x %>% mutate(id=1:n()) %>% 
  pivot_longer(-id) %>%
  separate_rows(value,sep = '\(') %>%
  separate_rows(value,sep = ',') %>%
  mutate(value=as.numeric(trimws(gsub(')','',value)))) %>%
  group_by(id,name) %>%
  mutate(name=ifelse(row_number()==1,paste0(name,'.est'),
                     ifelse(row_number()==2,paste0(name,'.low'),paste0(name,'.upper')))) %>%
  pivot_wider(names_from = name,values_from=value) %>% ungroup() %>% select(-id)
  

Вывод:

 # A tibble: 2 x 6
  Col1.est Col1.low Col1.upper Col2.est Col2.low Col2.upper
     <dbl>    <dbl>      <dbl>    <dbl>    <dbl>      <dbl>
1    0.465   0.406       0.752    0.200    0.138      0.350
2    0.414   0.0178      0.617    0.981    0.709      0.988
  

Ответ №2:

Мы можем использовать map для перебора имен столбцов и extract столбца в разные столбцы

 library(dplyr)
library(tidyr)
library(purrr)
library(stringr)
map_dfc(names(x), ~ x %>% 
     select(.x) %>%
      extract(!!rlang::sym(.x),
      into = str_c(.x, c('est', 'lower', 'Upper')),
     '^([0-9.] )\s \(([0-9.] ),\s ([0-9.] )\).*', convert = TRUE) ) %>%
  as_tibble
  

-вывод

 # A tibble: 2 x 6
#  Col1est Col1lower Col1Upper Col2est Col2lower Col2Upper
#    <dbl>     <dbl>     <dbl>   <dbl>     <dbl>     <dbl>
#1   0.465    0.406      0.752   0.200     0.138     0.350
#2   0.414    0.0178     0.617   0.981     0.709     0.988
  

ПРИМЕЧАНИЕ: это возвращает правильный тип столбца


Или вариант с separate

 x %>% 
   mutate(across(everything(), ~ str_remove_all(., '[(),]'))) %>%
   separate(Col1, into = c('Col1est', 'Col1lower', 'Col1Upper'), sep=" ") %>%
   separate(Col2, into = c('Col2est', 'Col2lower', 'Col2Upper'), sep=" ")       
  

-вывод

 #  Col1est Col1lower Col1Upper Col2est Col2lower Col2Upper
#1  0.4646    0.4061    0.7522  0.1996    0.1383    0.3499
#2  0.4137    0.0178     0.617  0.9814    0.7092    0.9884
  

Или с помощью cSplit from splitstackshape

 library(data.table)
library(splitstackshape)
cSplit(setDT(x)[, lapply(.SD, gsub, pattern = '[(),]', 
         replacement = "")], c('Col1', 'Col2'), sep=" ")
  

-вывод

 #   Col1_1 Col1_2 Col1_3 Col2_1 Col2_2 Col2_3
#1: 0.4646 0.4061 0.7522 0.1996 0.1383 0.3499
#2: 0.4137 0.0178 0.6170 0.9814 0.7092 0.9884
  

Или используя base R , мы можем использовать gsub с read.table

 f1 <- function(colval) {
   read.table(text = gsub("[(),]", " ", colval), header = FALSE)
  }

out <- do.call(cbind, lapply(x, f1))
names(out) <- paste0(sub("\..*", "", names(out)),
            rep(c("est", "lower", "Upper"), 2))
  

-вывод

 out
#  Col1est Col1lower Col1Upper Col2est Col2lower Col2Upper
#1  0.4646    0.4061    0.7522  0.1996    0.1383    0.3499
#2  0.4137    0.0178    0.6170  0.9814    0.7092    0.9884
  

Ответ №3:

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

 library(dplyr)
library(tidyr)

x %>%
  mutate(row = row_number()) %>%
  pivot_longer(cols = -row) %>%
  extract(value, c('est', 'lower', 'upper'), '(.*)\s\((.*),(.*)\)', 
          convert = TRUE) %>%
  pivot_wider(names_from = name, values_from = est:upper, 
              names_glue = '{name}_{.value}') %>%
  select(-row)

#  Col1_est Col2_est Col1_lower Col2_lower Col1_upper Col2_upper
#     <dbl>    <dbl>      <dbl>      <dbl>      <dbl>      <dbl>
#1    0.465    0.200     0.406       0.138      0.752      0.350
#2    0.414    0.981     0.0178      0.709      0.617      0.988
  

Ответ №4:

Для полноты картины, вот подход, который использует tstrsplit() функцию from data.table . Он работает без изменения формы / поворота для произвольного количества входных столбцов:

 library(data.table)
library(magrittr) # piping used to improve readability
new_cols <- c("est", "lower", "upper")
setDT(x)[, lapply(.SD, function(x) tstrsplit(x, "[(),]") 
                    %>% setDT() %>% setnames(new_cols))]

     
  
    Col1.est Col1.lower Col1.upper Col2.est Col2.lower Col2.upper
1:  0.4646      0.4061     0.7522  0.1996      0.1383     0.3499
2:  0.4137      0.0178      0.617  0.9814      0.7092     0.9884
  

Выходные столбцы по-прежнему имеют тип character . В случае, если ожидаются числовые выходные столбцы, приведение может быть включено в канал:

 setDT(x)[, lapply(.SD, function(x) tstrsplit(x, "[(),]") %>% 
                    lapply(as.numeric) %>% setDT() %>% setnames(new_cols))]
  
    Col1.est Col1.lower Col1.upper Col2.est Col2.lower Col2.upper
1:   0.4646     0.4061     0.7522   0.1996     0.1383     0.3499
2:   0.4137     0.0178     0.6170   0.9814     0.7092     0.9884