在 R 中,如何使用元数据的第二个文件将标签值分解并添加到特定的 data.table 列?
In R how do you factorise and add label values to specific data.table columns, using a second file of meta data?
这是从 SPSS 切换到 R 的项目的一部分。虽然有很好的工具可以将 SPSS 文件导入 R (expss),但这个问题的一部分是试图在数据时获得 SPSS 样式标签的好处源自 CSV 源。这是为了帮助弥合 SPSS 和 R 之间的员工培训差距,为 data.tables 提供一种通用格式,而不管文件格式来源如何。
虽然 CSV 可以合理地存储数据,但无法提供有意义的数据。这不可避免地意味着变量和因子水平以及标签必须来自其他地方。在大多数简短示例中(例如在文档中),简单地硬编码元数据是可行的。但是对于较大的项目,将此元数据存储在第二个 csv 文件中更有意义。
示例数据文件
ID,varone,vartwo,varthree,varfour,varfive,varsix,varseven,vareight,varnine,varten
1,1,34,1,1,1,1,4,
2,1,21,0,1,1,3,14,3,2
3,1,54,1,1,3,6,4,4
4,2,32,1,1,1,3,7,4,
5,3,66,0,1,3,9,3,3
6,2,43,1,1,1,12,2,1
7,2,26,0,1,2,11,1,
8,3,1,1,2,15,1,4
9,1,34,1,1,1,12,3,4
10,2,46,0,3,13,2,
11,3,39,1,1,1,3,7,1,2
12,1,28,0,1,1,6,5,1
13,2,64,0,1,2,11,3
14,3,34,1,1,3,10,1,1
15,1,52,1,1,1,1,8,6,
示例元数据文件
Rowlabels,ID,varone,vartwo,varthree,varfour,varfive,varsix,varseven,vareight,varnine,varten
varlabel,问题一,问题二,问题三,问题四,问题五,问题六,问题七,问题八,问题九,问题十
varrole,独特的,态度,独特的,过滤器,过滤器,过滤器,过滤器,态度,过滤器,态度,态度
丢失,错误,错误,忽略,错误,未检查,未检查,未检查,错误,错误,错误,已忽略
vallable,One,No,Checked,Checked,Checked,x,One,A,支持
vallable,Two,Yes,y,Two,B,Neutral
vallable,Three,z,Three,C,反对
vallable,四,D,不知道
vallable,五,E,
vallable,六,F,
vallable,七,G,
vallable,八,
vallable,九个,
vallable,十,
vallable,十一,
vallable,十二,
vallable,十三,
vallable,十四,
vallable,十五岁,
所以共同的元素是列名,它们是两个文件的键
元数据文件的第一列描述了该行对于数据文件的作用
所以
varlabel 为每一列提供变量标签
varrole 描述变量的解析目的
missing 描述了如何处理丢失的数据
varlabel 描述一个因子水平的标签,从一个开始到尽可能多的标签。
对了!这是有效的代码:
```#Libraries
library(expss)
library(data.table)
library(magrittr)```
readcsvdata <- function(dfile)
{
# TESTED - Working
print("OK Lets read some comma separated values")
rdata <- fread(file = dfile, sep = "," , quote = "\"" , header = TRUE, stringsAsFactors = FALSE,
na.strings = getOption("datatable.na.strings",""))
return(rdata)
}
rawdatafilename <- "testdata.csv"
rawmetadata <- "metadata.csv"
mdt <- readcsvdata(rawmetadata)
rdt <- readcsvdata(rawdatafilename)
names(rdt)[names(rdt) == "ï..ID"] <- "ID" # correct minor data error
commonnames <- intersect(names(mdt),names(rdt)) # find common variable names so metadata applies
commonnames <- commonnames[-(1)] # remove ID
qlabels <- as.list(mdt[1, commonnames, with = FALSE])
(这里我简单地复制了 rdt 数据table 这样我就可以回滚到原始数据而无需重新 运行 以前的读取块并在我进行不起作用的更改时进行整理出。
# set var names to columns
for (each_name in commonnames) # loop through commonnames and qlabels
{
expss::var_lab(tdt[[each_name]]) <- qlabels[[each_name]]
}
好的,这就是我摔倒的地方。
从这里开始失败
factorcols <- as.vector(commonnames) # create a vector of column names (for later use)
for (col in factorcols)
{
print( is.na(mdt[4, ..col])) # print first row of value labels (as test)
if (is.na(mdt[4, ..col])) factorcols <- factorcols[factorcols != col]
# if not a factor column, remove it from the factorcol list and dont try to factor it
else { # if it is a vector factorise
print(paste("working on",col)) # I have had a lot of problem with unrecognised ..col variables
tlabels <- as.vector(na.omit(mdt[4:18, ..col])) # get list of labels from the data column}
validrange <- seq(1,lengths(tlabels),1) # range of valid values is 1 to the length of labels list
print(as.character(tlabels)) # for testing
print(validrange) # for testing
tdt[[col]] <- factor(tdt[[col]], levels = validrange, ordered = is.ordered(validrange), labels = as.character(tlabels))
# expss::val_lab(tdt[, ..col]) <- tlabels
tlabels = c() # flush loop variable
validrange = c() # flush loop variable
}
}
所以在查资料的时候发现问题就在这里table.
tdt
标签已作为整个向量应用于每个列条目,除非向量中只有一个值("checked" for varfour 和 varfive)
tdt
id (int) 1
varone (fctr) c("One", "Two", "Three") 1 (应该是"One" 1)
vartwo(S3:标记)34
varthree (fctr) c("No", "Yes") 1 (应该是"No" 1)
varfour (fctr) NA
varfive (fctr) 选中
还有一个谜
当我不使用 for 循环变量
时,这段代码在单个列上工作得很好
# test using column name
tlabels <- c("one","two","three")
validrange <- c(1,2,3)
factor(tdt[,varone], levels = validrange, ordered=is.ordered(validrange), labels = tlabels)
问题似乎出在 tlabels <- as.vector(na.omit(mdt[4:18, ..col]))
行。它不会像您期望的那样制作矢量。与通常情况相反,当您在索引中提供单列时,data.frame data.table 不会删除维度。 as.vector
对 data.frames/data.tables 什么也不做。所以 tlabels
仍然是 data.table。此行需要改写为 tlabels <- na.omit(mdt[[col]][4:18])
。
示例:
library(data.table)
mdt = as.data.table(mtcars)
col = "am"
tlabels <- as.vector(na.omit(mdt[3:6, ..col])) # ! tlabels is data.table
str(tlabels)
# Classes ‘data.table’ and 'data.frame': 4 obs. of 1 variable:
# $ am: num 1 0 0 0
# - attr(*, ".internal.selfref")=<externalptr>
as.character(tlabels) # character vector of length 1
# [1] "c(1, 0, 0, 0)"
tlabels <- na.omit(mdt[[col]][3:6]) # vector
str(tlabels)
# num [1:4] 1 0 0 0
as.character(tlabels) # character vector of length 4
# [1] "1" "0" "0" "0"
这是从 SPSS 切换到 R 的项目的一部分。虽然有很好的工具可以将 SPSS 文件导入 R (expss),但这个问题的一部分是试图在数据时获得 SPSS 样式标签的好处源自 CSV 源。这是为了帮助弥合 SPSS 和 R 之间的员工培训差距,为 data.tables 提供一种通用格式,而不管文件格式来源如何。
虽然 CSV 可以合理地存储数据,但无法提供有意义的数据。这不可避免地意味着变量和因子水平以及标签必须来自其他地方。在大多数简短示例中(例如在文档中),简单地硬编码元数据是可行的。但是对于较大的项目,将此元数据存储在第二个 csv 文件中更有意义。
示例数据文件
ID,varone,vartwo,varthree,varfour,varfive,varsix,varseven,vareight,varnine,varten 1,1,34,1,1,1,1,4, 2,1,21,0,1,1,3,14,3,2 3,1,54,1,1,3,6,4,4 4,2,32,1,1,1,3,7,4, 5,3,66,0,1,3,9,3,3 6,2,43,1,1,1,12,2,1 7,2,26,0,1,2,11,1, 8,3,1,1,2,15,1,4 9,1,34,1,1,1,12,3,4 10,2,46,0,3,13,2, 11,3,39,1,1,1,3,7,1,2 12,1,28,0,1,1,6,5,1 13,2,64,0,1,2,11,3 14,3,34,1,1,3,10,1,1 15,1,52,1,1,1,1,8,6,
示例元数据文件
Rowlabels,ID,varone,vartwo,varthree,varfour,varfive,varsix,varseven,vareight,varnine,varten varlabel,问题一,问题二,问题三,问题四,问题五,问题六,问题七,问题八,问题九,问题十 varrole,独特的,态度,独特的,过滤器,过滤器,过滤器,过滤器,态度,过滤器,态度,态度 丢失,错误,错误,忽略,错误,未检查,未检查,未检查,错误,错误,错误,已忽略 vallable,One,No,Checked,Checked,Checked,x,One,A,支持 vallable,Two,Yes,y,Two,B,Neutral vallable,Three,z,Three,C,反对 vallable,四,D,不知道 vallable,五,E, vallable,六,F, vallable,七,G, vallable,八, vallable,九个, vallable,十, vallable,十一, vallable,十二, vallable,十三, vallable,十四, vallable,十五岁,
所以共同的元素是列名,它们是两个文件的键
元数据文件的第一列描述了该行对于数据文件的作用 所以 varlabel 为每一列提供变量标签 varrole 描述变量的解析目的 missing 描述了如何处理丢失的数据 varlabel 描述一个因子水平的标签,从一个开始到尽可能多的标签。
对了!这是有效的代码:
```#Libraries
library(expss)
library(data.table)
library(magrittr)```
readcsvdata <- function(dfile)
{
# TESTED - Working
print("OK Lets read some comma separated values")
rdata <- fread(file = dfile, sep = "," , quote = "\"" , header = TRUE, stringsAsFactors = FALSE,
na.strings = getOption("datatable.na.strings",""))
return(rdata)
}
rawdatafilename <- "testdata.csv"
rawmetadata <- "metadata.csv"
mdt <- readcsvdata(rawmetadata)
rdt <- readcsvdata(rawdatafilename)
names(rdt)[names(rdt) == "ï..ID"] <- "ID" # correct minor data error
commonnames <- intersect(names(mdt),names(rdt)) # find common variable names so metadata applies
commonnames <- commonnames[-(1)] # remove ID
qlabels <- as.list(mdt[1, commonnames, with = FALSE])
(这里我简单地复制了 rdt 数据table 这样我就可以回滚到原始数据而无需重新 运行 以前的读取块并在我进行不起作用的更改时进行整理出。
# set var names to columns
for (each_name in commonnames) # loop through commonnames and qlabels
{
expss::var_lab(tdt[[each_name]]) <- qlabels[[each_name]]
}
好的,这就是我摔倒的地方。 从这里开始失败
factorcols <- as.vector(commonnames) # create a vector of column names (for later use)
for (col in factorcols)
{
print( is.na(mdt[4, ..col])) # print first row of value labels (as test)
if (is.na(mdt[4, ..col])) factorcols <- factorcols[factorcols != col]
# if not a factor column, remove it from the factorcol list and dont try to factor it
else { # if it is a vector factorise
print(paste("working on",col)) # I have had a lot of problem with unrecognised ..col variables
tlabels <- as.vector(na.omit(mdt[4:18, ..col])) # get list of labels from the data column}
validrange <- seq(1,lengths(tlabels),1) # range of valid values is 1 to the length of labels list
print(as.character(tlabels)) # for testing
print(validrange) # for testing
tdt[[col]] <- factor(tdt[[col]], levels = validrange, ordered = is.ordered(validrange), labels = as.character(tlabels))
# expss::val_lab(tdt[, ..col]) <- tlabels
tlabels = c() # flush loop variable
validrange = c() # flush loop variable
}
}
所以在查资料的时候发现问题就在这里table.
tdt
标签已作为整个向量应用于每个列条目,除非向量中只有一个值("checked" for varfour 和 varfive)
tdt
id (int) 1
varone (fctr) c("One", "Two", "Three") 1 (应该是"One" 1)
vartwo(S3:标记)34
varthree (fctr) c("No", "Yes") 1 (应该是"No" 1)
varfour (fctr) NA
varfive (fctr) 选中
还有一个谜
当我不使用 for 循环变量
# test using column name
tlabels <- c("one","two","three")
validrange <- c(1,2,3)
factor(tdt[,varone], levels = validrange, ordered=is.ordered(validrange), labels = tlabels)
问题似乎出在 tlabels <- as.vector(na.omit(mdt[4:18, ..col]))
行。它不会像您期望的那样制作矢量。与通常情况相反,当您在索引中提供单列时,data.frame data.table 不会删除维度。 as.vector
对 data.frames/data.tables 什么也不做。所以 tlabels
仍然是 data.table。此行需要改写为 tlabels <- na.omit(mdt[[col]][4:18])
。
示例:
library(data.table)
mdt = as.data.table(mtcars)
col = "am"
tlabels <- as.vector(na.omit(mdt[3:6, ..col])) # ! tlabels is data.table
str(tlabels)
# Classes ‘data.table’ and 'data.frame': 4 obs. of 1 variable:
# $ am: num 1 0 0 0
# - attr(*, ".internal.selfref")=<externalptr>
as.character(tlabels) # character vector of length 1
# [1] "c(1, 0, 0, 0)"
tlabels <- na.omit(mdt[[col]][3:6]) # vector
str(tlabels)
# num [1:4] 1 0 0 0
as.character(tlabels) # character vector of length 4
# [1] "1" "0" "0" "0"