Можно ли с помощью R expss и data.table загружать метки data.table из файла csv вместо того, чтобы вводить код вручную? - PullRequest
2 голосов
/ 27 мая 2020

Применение ярлыков - важная часть обеспечения понятности данных опроса в отчетах

Итак, лучший пример, который я могу найти, использует expss :: apply_labels (), например, знаменитый пример mtcars https://cran.r-project.org/web/packages/expss/vignettes/tables-with-labels.html

в качестве входных данных для этого требуются таблица data.table и список пар присваиваний, разделенных запятыми, например,

apply_labels(dt, col1 = "label1", col2 = "label2", col3 = "label3")

Это нормально, если у вас есть один файл данных и несколько столбцов, и вас могут беспокоить вводить их каждый раз, но это не очень полезно, если у вас много файлов данных. Итак, как можно загрузить файл метаданных csv в формате:

Col1 Col2 Col3

Label1 Label2 Label3

, где имена Col соответствуют тем же именам в таблице данных

это означает эффективное преобразование CSV-файла метаданных так, чтобы он генерировал

col n = "label n "

для каждого столбца.

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

Вот где я попал в

    library(expss)
    library(data.table)
    library(glue)

    readcsvdata <- function(dfile)
     {
        rdata <- fread(file = dfile, sep = "," , quote = "\"" , header = TRUE, 
        stringsAsFactors = FALSE, na.strings = getOption("datatable.na.strings","NA"))
        return(rdata)
        }

    rawdatafilename <- "testdata.csv"
    rawmetadata <- "metadata.csv"

    mdt <- readcsvdata(rawmetadata)
    rdt <-readcsvdata(rawdatafilename)
    commonnames <- intersect(names(mdt),names(rdt))  # find common 
    qlabels <- as.character(mdt[1, commonnames, with = FALSE])

    comslist <- list()
    for (i in 1:length(commonnames)) # loop through commonnames and qlabels
          {  
          if (i == length(commonnames))
              {x <- glue('{commonnames[i]} = "{qlabels[i]}"')} # no comma for final item
              else 
              {x <- glue('{commonnames[i]} = "{qlabels[i]}",')} # comma for next item

          comslist[[i]] <- x
    }

comstring <- paste(unlist(comslist), collapse = '')

tdt = apply_labels(tdt, eval(parse(text = comstring)))

, что дает

Ошибка при синтаксическом анализе (text = comstring):: 1: 24: неожиданно ',' 1: varone = "Label1", ^

oh, и print (comstring) выдаст:

[1] "varone = \" Вопрос один \ ", vartwo = \" Вопрос два \ ", varthree = \" Вопрос три \ ", varfour = \" Вопрос четыре \ ", varfive = \" Вопрос пять \ ", varsix = \" Вопрос шесть \ ", varseven = \" Вопрос семь \ ", vareight = \ "Вопрос восемь \", varnine = \ "Вопрос девять \", varten = \ "Вопрос десять \" "

Ответы [ 2 ]

1 голос
/ 30 мая 2020

apply_labels не очень удобно для присвоения меток из внешнего словаря. Вместо этого вы можете использовать var_lab:

library(expss)
library(data.table)

readcsvdata <- function(dfile)
{
    rdata <- fread(file = dfile, sep = "," , quote = "\"" , header = TRUE, 
                   stringsAsFactors = FALSE, na.strings = getOption("datatable.na.strings","NA"))
    return(rdata)
}

rawdatafilename <- "testdata.csv"
rawmetadata <- "metadata.csv"

mdt <- readcsvdata(rawmetadata)
rdt <-readcsvdata(rawdatafilename)
commonnames <- intersect(names(mdt),names(rdt))  # find common 
qlabels <- as.list(mdt[1, commonnames, with = FALSE])


for (each_name in commonnames) # loop through commonnames and qlabels
{  
    var_lab(rdt[[each_name]]) <- qlabels[[each_name]]
}

Аналогичная функция val_lab существует для меток значений. Дополнительно вас могут заинтересовать функции apply_dictionary и create_dictionary. Чтобы получить справку по ним, наберите в консоли ?apply_dictionary.

1 голос
/ 27 мая 2020

У меня нет expss под рукой, но я думаю, что в целом речь идет о том, как программно назначать аргументы функции в R.

Если вы начнете с файла CSV, который содержит три пары, которые вам нужны,

csvdat <- read.csv(stringsAsFactors=FALSE, text="
col1,col2,col3
label1,label2,label3")

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

my_fake_labels <- function(x, ...) {
  dots <- list(...)
  message("x labels   : ", paste(sQuote(colnames(x)), collapse = ", "))
  message("other names: ", paste(sQuote(names(dots)), collapse = ", "))
}
origDT <- data.table(aa=1, bb=2)

my_fake_labels(origDT, col1="label1", col2="label2", col3="label3")
# x labels   : 'aa', 'bb'
# other names: 'col1', 'col2', 'col3'

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

Программный c способ сделать это, используя origDT в качестве первого аргумента, и элементы csvdat в качестве второго и последующих аргументов:

do.call(my_fake_labels, c(list(origDT), csvdat))
# x labels   : 'aa', 'bb'
# other names: 'col1', 'col2', 'col3'

Второй аргумент для do.call должен быть list с необязательным именем. Поскольку data.frame (и, следовательно, data.table) - это просто прославленное имя list, это отвечает всем требованиям. Это делает каждый элемент списка и применяет его как соответствующие аргументы функции (первый аргумент do.call).

list(origDT) потому, что обычно функция c(...) объединяет столбцы / элементы двух списков. Если бы мы сделали только c(origDT, csvdat), тогда функция вызывалась бы с ncol(origDT) + ncol(csvdat) аргументами вместо желаемых 1 + ncol(csvdat). Для этого c(list(origDT), ...) проверяет, что весь origDT является первым аргументом функции.

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

...