如何在R中创建、构建、维护和更新数据编码手册?

24

为了进行复制,我喜欢保留一个包含每个数据框元数据的代码库。 数据代码库是:

一个书面或计算机化的列表,提供将包括在数据库中的变量的清晰而全面的描述。 Marczyk等人2010年

我喜欢记录变量的以下属性:

  • 名称
  • 描述(标签、格式、刻度等)
  • 来源(例如世界银行)
  • 来源媒体(url和访问日期、CD和ISBN等)
  • 磁盘上源数据的文件名(有助于合并代码库时)
  • 备注

例如,这是我正在实施的用于记录具有8个变量的数据框mydata1中变量的方式:

code.book.mydata1 <- data.frame(variable.name=c(names(mydata1)),
     label=c("Label 1",
              "State name",
              "Personal identifier",
              "Income per capita, thousand of US$, constant year 2000 prices",
              "Unique id",
              "Calendar year",
              "blah",
              "bah"),
      source=rep("unknown",length(mydata1)),
      source_media=rep("unknown",length(mydata1)),
      filename = rep("unknown",length(mydata1)),
      notes = rep("unknown",length(mydata1))
)

我为每个读取的数据集编写不同的代码手册。当我合并数据框时,我还将合并其相关联的代码手册的相关方面,以记录最终数据库。我通过复制粘贴上面的代码并更改参数来实现这一点。


1
这里曾经有类似的问题被提出 here - Fred
5个回答

7
你可以使用 attr 函数向任何 R 对象添加特殊属性。例如:
x <- cars
attr(x,"source") <- "Ezekiel, M. (1930) _Methods of Correlation Analysis_.  Wiley."

查看对象结构中的给定属性:

> str(x)
'data.frame':   50 obs. of  2 variables:
 $ speed: num  4 4 7 7 8 9 10 10 10 11 ...
 $ dist : num  2 10 4 22 16 10 18 26 34 17 ...
 - attr(*, "source")= chr "Ezekiel, M. (1930) _Methods of Correlation Analysis_.  Wiley."

还可以使用相同的attr函数加载指定的属性:

> attr(x, "source")
[1] "Ezekiel, M. (1930) _Methods of Correlation Analysis_.  Wiley."

如果您只向数据框中添加新的案例,则给定的属性将不会受到影响(请参见:str(rbind(x,x)))。而更改结构将擦除给定的属性(请参见:str(cbind(x,x)))。


更新:基于评论

如果您想列出所有非标准属性,请参阅以下内容:

setdiff(names(attributes(x)),c("names","row.names","class"))

这将列出所有非标准属性(标准属性为:数据框中的名称、行名、类)。
基于此,您可以编写一个简短的函数来列出所有非标准属性以及其值。以下内容可行,但不太整洁...您可以改进它并创建一个函数 :)
首先,定义唯一(=非标准)属性:
uniqueattrs <- setdiff(names(attributes(x)),c("names","row.names","class"))

创建一个矩阵来存储名称和值:

attribs <- matrix(0,0,2)

循环遍历非标准属性,并将名称和值保存在矩阵中:

for (i in 1:length(uniqueattrs)) {
    attribs <- rbind(attribs, c(uniqueattrs[i], attr(x,uniqueattrs[i])))
}

将矩阵转换为数据框并命名列:

attribs <- as.data.frame(attribs)
names(attribs) <- c('name', 'value')

并以任何格式保存,例如:

write.csv(attribs, 'foo.csv')

关于变量标签的问题,可以查看foreign包中的read.spss函数,因为它恰好实现了你需要的功能:将值标签保存在attrs部分。主要思路是,一个attr可以是数据帧或其他对象,因此您不需要为每个变量制作唯一的“attr”,而只需制作一个(例如命名为“variable labels”),并在其中保存所有信息。您可以调用类似于:attr(x,"variable.labels")['foo'],其中'foo'代表所需的变量名称。但请查看上面引用的函数以及导入的数据框架属性,以获取更多详细信息。
希望这些内容能够帮助您比我之前尝试的方式更好地编写所需函数! :)

@daroczig 很棒!非常感谢!如果我可以追问一下:如何修改您的语句 attr(x, "source"),以便将属性名称(source)和属性值(“Ezekiel,M。(1930)_Methods of Correlation Analysis_。Wiley。”)并排打印并导出到 .csv 文件中? - Fred
@Fred:我在我的回答中添加了更多细节,希望对你有所帮助。作为一个自学的R语言学习者,我的知识有限,我的回答可能无法满足你的所有需求,但我希望它能让你更接近目标。 - daroczig
感谢daroczig的更新。这非常有用。它确实帮助我以更有结构的方式思考所需的函数和一些可能性。 - Fred
使用attr的一个明显优点是数据和元数据被链接在一起。但是需要测试这种链接与我现在拥有的单独元数据框架相比稳定/方便程度如何。我是R的新手,所以现在没有任何先验知识。其他更有经验的人可能会对此有更深入的了解。 - Fred
@Fred:通过将数据和元数据链接在一起,并通过“save”保存R对象而不是第三方格式,将确保您的元数据始终可访问。但是,如果您的同事不使用R,则没有任何收益。这样,我会在任何统计软件之外的数据库中收集所有元数据(例如,在在线团队协作解决方案中),并始终将这些数据与精确数据集并行导入。但无论如何,在这种情况下,将元数据链接(甚至加载)到任何统计软件中是否有意义?只有在编写检定时加载元数据才有趣。 - daroczig
显示剩余3条评论

5

截至2020年,有专门用于码书的R软件包,可能符合您的需求。

  • codebooks包是一个全面的包,可以生成不同格式的代码书(包括常用属性和描述性统计)。它有一个网站和一篇论文(Arslan,2019年,如何使用codebook软件包自动记录数据以促进数据重复使用),在第1图中,还比较了不同方法。
    这里有一个示例

  • dataspice包(由rOpenSci推荐)专门用于生成可以在网络上被搜索引擎找到的元数据。它有一个网站
    这里有一个示例

  • dataMaid包可以生成包含元数据和描述性统计信息的报告,并进行某些检查。它在CRAN和GitHub上,并且有一篇JSS论文(Petersen和Ekstrøm,2019年,dataMaid:您的助手,用于在R中记录受监督数据质量筛查)。
    这里有一个示例

  • memisc包具有处理调查数据的许多功能,并附带一个代码书函数。它有一个网站
    这里有一个示例

  • 还有一篇由Marta Kołczyńska撰写的博客文章,其中有一个轻量级函数,可以生成包含元数据的数据框(例如,可以导出到Excel文件)。
    这里有一个示例


5

更高级的版本是使用S4类。例如,在Bioconductor中,ExpressionSet被用来存储带有相关实验元数据的微阵列数据。

第4.4节中描述的MIAME对象看起来非常类似于您想要的内容:

experimentData <- new("MIAME", name = "Pierre Fermat",
          lab = "Francis Galton Lab", contact = "pfermat@lab.not.exist",
          title = "Smoking-Cancer Experiment", abstract = "An example ExpressionSet",
          url = "www.lab.not.exist", other = list(notes = "Created from text files"))

现在也有 memisc 这个工具,它似乎只实现了这一点:用于调查和代码簿元数据的 S4 类。 - maxheld

5
< p > comment() 函数在这里可能会有用。它可以设置和查询对象的注释属性,但与其他普通属性不同的是,它不会被打印出来。

dat <- data.frame(A = 1:5, B = 1:5, C = 1:5)
comment(dat$A) <- "Label 1"
comment(dat$B) <- "Label 2"
comment(dat$C) <- "Label 3"
comment(dat) <- "data source is, sampled on 1-Jan-2011"

这将会给出:

> dat
  A B C
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
5 5 5 5
> dat$A
[1] 1 2 3 4 5
> comment(dat$A)
[1] "Label 1"
> comment(dat)
[1] "data source is, sampled on 1-Jan-2011"

合并示例:

> dat2 <- data.frame(D = 1:5)
> comment(dat2$D) <- "Label 4"
> dat3 <- cbind(dat, dat2)
> comment(dat3$D)
[1] "Label 4"

但这样会丢失对dat()的注释:
> comment(dat3)
NULL

那些操作需要明确处理。为了真正做到你想要的,你可能需要编写特殊版本的函数,以在提取/合并操作期间维护注释/元数据。或者你可能想要研究一下生成自己的对象类 - 比如一个带有数据框和其他组件的列表,并编写想要保留元数据的函数方法。
一个沿着这条线的例子是zoo包,它为时间序列生成一个列表对象,其中包含额外的组件,如排序和时间/日期信息等,但从子集等方面来看仍然像普通对象一样工作,因为作者已经为诸如[等函数提供了方法。

谢谢!合并将丢失对dat()的注释是可以预期的,甚至是期望的:合并的数据可能有两个不同的来源。攻击的一个角度是像melt一样处理它:在合并之前用数据框级别的注释填充变量级别的注释。在其他软件中,我已经将文档原子化到观察级别,当一个单位的记录已被切割或用来自其他来源的记录填充时,这是有用的(但通常这是过度的)。 - Fred

4
我处理此问题的方法略有不同,且显然不太技术化。我通常遵循一个指导原则,即如果文本不是为计算机设计的,而只有对人类有意义,那么它应该放在源代码的注释中。
这样做有一些好处:
  • 当其他人在未来接手你的代码时,注释明确表明它们是为他们阅读而写的。在数据结构中设置参数可能不明显,也不容易理解。
  • 跟踪抽象对象内部设置的参数需要相当多的纪律性。编写代码注释也需要纪律性,但缺少注释会立即显现出来。如果描述作为对象的一部分被传递,那么在浏览代码时就不容易看出来。代码就变得不那么“有文化”,从“有文化编程”的意义上讲。
  • 将数据描述放在数据对象内部很容易导致错误的描述。例如,如果包含千克测量值的列乘以2.2转换为磅,很容易忽略更新元数据的必要性。
显然,随着对象一起携带元数据也有一些真正的优点。如果你的工作流使上述观点不那么重要,那么创建一个元数据附件到你的数据结构中可能是有意义的。我的目的只是分享一些为什么“低技术”基于注释的方法可能会被考虑的原因。

谢谢!这些都是很好的观点。我并不总是如此仔细地记录我的工作,但对于大型协作项目、出版等,这是非常有用的。我的一些合作者不会碰 R。拥有代码书和扁平文件中的数据有助于协作工作。最后,在 Aremos 中,我会记录原始数据。从那里创建的任何数据都会自动标记使用的公式,因此您可以随时返回链条查看您所拥有的内容(例如,创建 y <- x*z 将为 y 创建一个标签字段,其读取 "y <- x*z")。 - Fred
Aremos 的自我记录功能非常棒。我之前并没有意识到它有这个功能。一切旧事重现!我的回答显然不是你问题的答案,更像是一个“值得考虑”的评论。感谢你以那种方式接受它。 - JD Long
我猜你的方法可能与_Sweave_结合使用会更好,因为代码中的任何相关注释都应该反映在最终文档中。这里的优点是其他协作者不需要阅读_R_脚本。缺点是_Latex_文档通常是在过程结束时准备的,而文档从一开始就开始编写。因此,Codebook + Sweave可能是理想的选择(如果费力的话)... - Fred
顺便提一下,原始数据文件和R脚本可能可以作为复制文件。但是,在复制一些已发表的工作时,我发现作者只提供他们最终的“分析”数据库。复制后者几乎是不可能的,因为大多数原始数据提供者没有版本控制系统。此外,分析数据通常是由一些RA组合而成,其脚本早已丢失。作者所拥有的只是分析脚本 - 对数据来源的信息很少 - 而且数据通常没有或者文档质量很差。不确定将这两个东西放在一起是否算作复制。 - Fred

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接