В R при создании имен для столбцов из текста с разделителями в другом столбце новые имена столбцов назначаются только из 1-й строки

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

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

например

  A  B                                       C E Domestic Glue_Sniffing NA NA NA NA NA NA NA
1 1  0 ;Domestic;;Domestic abuse;Glue Sniffing 7        1            NA NA NA NA NA NA NA NA
2 2  4                             ;Drug;Abus; 8       NA            NA  1  1 NA NA NA NA NA
3 3  6          ;Drug;Domestic Abuse;Domestic; 9        1            NA  1 NA NA NA NA NA NA
4 4  5                       ;Alcohol;;Verbal; 5       NA            NA NA NA  1  1 NA NA NA
5 5  7                      ;Shinpads;Abus ; ; 6       NA            NA NA  1 NA NA  1 NA NA
6 6  9                                         7       NA            NA NA NA NA NA NA NA NA
7 7 12                                   ;Ail; 8       NA            NA NA NA NA NA NA NA  1
8 8 10                                         7       NA            NA NA NA NA NA NA NA NA
9 9  9                                       ; 8       NA            NA NA NA NA NA NA NA NA

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

Куда я иду не так? Код соответствует шаблону, но изменен для добавления "=1" к каждому элементу с разделителями следующим образом:

#Define a function to take vectors like c("A=1","B=2") and changed them into named vectors like c(A="1", B="2").
createNamedVectors <- function(x) {

    a <- strsplit(x,"=")
    setNames(sapply(a,'[',2), sapply(a,'[',1))

}


tmp.df<-data.frame(
    A = c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9), 
    B = c(0L, 4, 6, 5L, 7L, 9L, 12L, 10L, 9), 
    C = c(";Ailment;Drug;Abus;Domestic;Domestic abuse;Glue Sniffing", 
          ";Drug;Abus;", 
          ";Drug;Domestic Abuse;Domestic;",
          ";Alcohol;;Verbal;",
          ";Shinpads; ;",
          "",
          ";Ail;",
          " ",
          ";"),
    D = c(";Vodka=2;Drug;Abus;", 
          ";Drug;Abu;", 
          ";Alcohol;Drug;Verbal;",
          ";Drug;Doms;",
          ";Shinpads;",
          " ",
          "",
          ";Ail;",
          "New"),
    E = c(7L, 8L, 9L, 5L, 6L, 7L, 8L, 7L, 8), 
    stringsAsFactors=T
)

DelimitedNamesOfNewCols <- str_replace_all(as.character(tmp.df$C),"Domestic [Aa]buse","Domestic")
DelimitedNamesOfNewCols <- str_replace_all(DelimitedNamesOfNewCols,";*[[:space:]]*;",";")
DelimitedNamesOfNewCols <- str_replace_all(DelimitedNamesOfNewCols,"^;","")
DelimitedNamesOfNewCols <- str_replace_all(DelimitedNamesOfNewCols,";","=1;")
DelimitedNamesOfNewCols <- str_replace_all(DelimitedNamesOfNewCols,"^[[:space:]]+","DUMMY=;")
DelimitedNamesOfNewCols <- str_replace_all(DelimitedNamesOfNewCols,"[[:space:]]+$","DUMMY=;")
DelimitedNamesOfNewCols <- str_replace_all(DelimitedNamesOfNewCols,"[[:space:]]","_")
DelimitedNamesOfNewCols <- str_replace_all(DelimitedNamesOfNewCols,"^$","DUMMY=;")
DelimitedNamesOfNewCols <- str_replace_all(DelimitedNamesOfNewCols,";$","")



ColsAndValsAsNamedVectors <-lapply(strsplit(DelimitedNamesOfNewCols,";"), createNamedVectors)


#Get list of all column names, then trim and remove NA and blanks
UniqueColumnNames <-unique(unlist(sapply(ColsAndValsAsNamedVectors, names)))
UniqueColumnNames <- stri_trim(UniqueColumnNames)
UniqueColumnNames <- UniqueColumnNames[!is.na(UniqueColumnNames)]
UniqueColumnNames <- UniqueColumnNames[stri_cmp_gt(UniqueColumnNames,"")]


#Extract data from all rows for every column
DataFromRowsForEachColumn <-do.call(rbind, lapply(ColsAndValsAsNamedVectors, '[', UniqueColumnNames))


#Convert everything to numeric
class(DataFromRowsForEachColumn)<-"numeric"

#Rejoin with original data.frame removing column 4
cbind(tmp.df[,-4], DataFromRowsForEachColumn)

Выполнение и проверка с помощью множества операторов отладки показывает, что имена и значения назначены правильно для всех записей, насколько это видно из следующего оператора

lapply(ColsAndValsAsNamedVectors, '[', UniqueColumnNames)

который находится в заявлении

DataFromRowsForEachColumn <- do.call(rbind, lapply(ColsAndValsAsNamedVectors, '[', UniqueColumnNames))

Однако в DataFromRowsForEachColumn есть только имена столбцов из первой строки.

Нужно ли мне возвращаться к чертежной доске, я сделал явно очевидную ошибку или есть более элегантное решение, которое мне не хватает?

Все ответы с благодарностью получены

2 ответа

Не уверен, правильно ли я вас понял, но для того, чтобы создавать новые столбцы из ; разделенный ряд, вы можете использовать фантастический tidyverse пакет:

library(tidyverse)

df %>%
  separate_rows(C, sep = ';') %>%
  filter (C != '') %>%
  mutate(new = 1) %>%
  spread(C, new)

Это дает

  A  B                     D E    Abus Ail Ailment Alcohol Domestic Domestic abuse Domestic Abuse Drug Glue Sniffing Shinpads Verbal
1 1  0   ;Vodka=2;Drug;Abus; 7 NA    1  NA       1      NA        1              1             NA    1             1       NA     NA
2 2  4            ;Drug;Abu; 8 NA    1  NA      NA      NA       NA             NA             NA    1            NA       NA     NA
3 3  6 ;Alcohol;Drug;Verbal; 9 NA   NA  NA      NA      NA        1             NA              1    1            NA       NA     NA
4 4  5           ;Drug;Doms; 5 NA   NA  NA      NA       1       NA             NA             NA   NA            NA       NA      1
5 5  7            ;Shinpads; 6  1   NA  NA      NA      NA       NA             NA             NA   NA            NA        1     NA
6 7 12                       8 NA   NA   1      NA      NA       NA             NA             NA   NA            NA       NA     NA
7 8 10                 ;Ail; 7  1   NA  NA      NA      NA       NA             NA             NA   NA            NA       NA     NA

Возможно, есть лучший способ сделать это, но ваш код почти работает, так что давайте его получим.

Как вы говорите, все хорошо, пока мы не приедем

# problem line
DataFromRowsForEachColumn <-do.call(rbind, lapply(ColsAndValsAsNamedVectors, '[', UniqueColumnNames))

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

lapply(ColsAndValsAsNamedVectors, '[', UniqueColumnNames)
# [[1]]
#       Ailment          Drug          Abus      Domestic Glue_Sniffing          <NA>          <NA>          <NA>          <NA>          <NA> 
#           "1"           "1"           "1"           "1"            NA            NA            NA            NA            NA            NA 
# 
# [[2]]
# <NA> Drug Abus <NA> <NA> <NA> <NA> <NA> <NA> <NA> 
#   NA  "1"  "1"   NA   NA   NA   NA   NA   NA   NA 
# 
# [[3]]
#     <NA>     Drug     <NA> Domestic     <NA>     <NA>     <NA>     <NA>     <NA>     <NA> 
#       NA      "1"       NA      "1"       NA       NA       NA       NA       NA       NA 
# 
# [[4]]
#    <NA>    <NA>    <NA>    <NA>    <NA> Alcohol  Verbal    <NA>    <NA>    <NA> 
#      NA      NA      NA      NA      NA     "1"     "1"      NA      NA      NA 
# 
# [[5]]
#     <NA>     <NA>     <NA>     <NA>     <NA>     <NA>     <NA> Shinpads     <NA>     <NA> 
#       NA       NA       NA       NA       NA       NA       NA      "1"       NA       NA 

Простое решение - просто исправить имена в результате:

colnames(DataFromRowsForEachColumn) = UniqueColumnNames

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


Другие комментарии:

Я очень скептически отношусь к вашему stri_trim линия - кажется, слишком поздно в вашем конвейере. Я думаю, вам нужно обрезать раньше (если это нужно). Похоже, что вы заботитесь об этом с вашим "^[[:space:]]+" а также "[[:space:]]+$" замена линий. Вы можете заменить эти 2 с str_trim, Но если у вас все еще есть пробелы, когда вы получаете уникальные имена столбцов, сделайте исправление выше по потоку.

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