R 编程/数据框操作
在本节中,我们将介绍如何读取、管理和清理数据框的方法。
在 R 中,数据框是相同长度的向量列表。它们不必是相同类型。例如,您可以将逻辑、字符和数值向量组合在一个数据框中。
如果数据已经以 R 格式(.Rda
或 .Rdata
)存在,您可以使用 load()
将它们加载到内存中。您可以使用 save()
将数据保存到 R 格式。
load("mydata.Rda")
save(list='mydata',file="mydata.Rda")
- 大多数包都包含示例数据集,用于测试函数。
data()
函数不带参数会显示所有已加载包中所有示例数据集的列表。- 如果您想将它们加载到内存中,只需使用 data 函数并将数据集名称作为参数传入即可。
str_data()
(sfsmisc)会显示包中所有数据集的结构。
> data() # lists all the datasets in all the packages in memory
> data(package="datasets") # lists all the datasets in the "datasets" package
> data(Orange) # loads the orange dataset in memory
> ?Orange # Help for the "Orange" Datasets
> str_data("datasets") # gives the structure of all the datasets in the datasets package.
可以使用向量创建数据框。
N <- 100
u <- rnorm(N)
x1 <- rnorm(N)
x2 <- rnorm(N)
y <- 1 + x1 + x2 + u
mydat <- data.frame(y,x1,x2)
R 拥有类似电子表格的数据编辑器。可以使用它将数据输入电子表格。
mydat <- edit(data.frame())
从剪贴板读取表格
> mydat <- read.table("clipboard")
还可以使用 `gsource()`(**Zelig**)在代码中读取以空格分隔的表格。以下是使用 Yule 1899 数据的示例。[3]
mydat <- gsource(var.names = "id union pauperism out old pop",
variables = "
1 Kensington 27 5 104 136
2 Paddington 47 12 115 111
3 Fulham 31 21 85 174
")
可以更改数据框的列名。
c1 <- c('A','B','C')
c2 <- c('Alpha','Bravo','Charlie')
c3 <- c('1','2','3')
mydf <- data.frame(c1,c2,c3)
colnames(mydf) <- c('ColName1','ColName2','ColName3')
有多种方法可以检查数据框,例如
- `str(df)` 提供数据的简要描述
- `names(df)` 提供每个变量的名称
- `summary(df)` 提供每个变量的一些基本摘要统计信息
- `head(df)` 显示前几行
- `tail(df)` 显示最后几行。
- 可以使用 `View()` 在电子表格中浏览数据。根据操作系统,此选项并非始终可用,并且结果也不尽相同。
- 可以使用 `head()` 打印前几行,使用 `tail()` 打印最后几行。
View(mydata)
head(mydata, n = 20) # n = 20 means that the first 20 lines are printed in the R console
- RStudio 拥有一个不错的 数据浏览器 (`View(mydata)`)。
- RKward 也拥有一个不错的 数据浏览器
- Paul Murrell 正在开发 **rdataviewer** 包 (pdf).
在处理数据框时,大多数情况下会更改数据,其中一项常见操作是添加列或行,从而增加数据框的维度。有多种方法可以实现,但最简单的方法是 `cbind()` 和 `rbind()`,它们是 **base** 包的一部分。
mydata <- cbind(mydata, newVector)
mydata <- rbind(mydata, newVector)
请记住,newVector 的长度应与要附加其到的数据框一侧的长度匹配。例如,在 `cbind()` 命令中,以下语句应为 TRUE
dim(mydata)[1]==length(newVector)
要查看更多示例,可以随时执行 `?base::cbind` 和 `?base::rbind`。
R 相比于 Stata 的一大优势是可以同时处理多个数据集。只需要指定数据集名称,并在每个变量名前面添加一个“$”符号(例如 `mydat1$var1` 和 `mydat2$var1`)。如果只处理一个数据集,并且不想每次都将数据集名称作为前缀添加到每个变量,可以使用 `attach()`。
mydata$var1
attach(mydata)
var1
detach(mydata)
在清理数据集时,经常需要检查数据中是否存在重复的信息。R 提供了一些函数来检测重复项。
- `duplicated()` 会查找重复元素并返回逻辑向量。可以使用 `table()` 对该向量进行汇总。
- `Duplicated()`(**sfsmisc**)对该命令进行了泛化。`Duplicated()` 仅将唯一值标记为“NA”。
- `remove.dup.rows()`(**cwhmisc**)。
- `unique()` 仅保留数据集中唯一的行。
- `distinct()`(**dplyr**)仅保留数据集中唯一的/不同的行。
library("Zelig")
mydat <- gsource(
variables = "
1 1 1 1
1 1 1 1
1 2 3 4
1 2 3 4
1 2 2 2
1 2 3 2")
unique(mydat) # keep unique rows
library(cwhmisc)
remove.dup.rows(mydat) # similar to unique()
table(duplicated(mydat)) # table duplicated lines
mydat$dups <- duplicated(mydat) # add a logical variable for duplicates
要创建新变量
mydata$newvar <- oldvar
如果要删除数据集中某个变量,可以将 NULL 指定给该变量
# Delete the x variable in the df data frame.
df$x <- NULL
- 可以通过重新定义数据框的名称向量来重命名变量。
- **reshape** 包中还提供了 `rename()` 函数。
df <- data.frame(x = 1:10, y = 21:30)
names(df)
names(df) <- c("toto","tata")
names(df)
names(df)[2] <- "titi"
names(df)
可以使用 `subset()` 对数据进行子集化。第一个参数是数据集名称,第二个参数是逻辑条件,用于指定要包含在新数据集中哪些行,最后一个参数是要包含在新数据集中哪些变量的列表。
在以下示例中,我们将生成一个虚假数据集,并使用 `subset()` 命令选择感兴趣的行和列。我们选择 `x1 > 0` 且 `x2 < 0` 的行,并将 `x1` 和 `x2` 作为变量保留。
N <- 100
x1 <- rnorm(N)
x2 <- 1 + rnorm(N) + x1
x3 <- rnorm(N) + x2
mydat <- data.frame(x1,x2,x3)
subset(x = mydat, subset = x1 > 0 & x2 < 0, select = c(x1,x2))
subset(x = mydat, subset = x1 > 0 & x2 < 0, select = - x3) # the same.
还可以使用 `select` 选项重新排列列。
subset(x = mydat, subset = x1 > 0 & x2 < 0, select = c(x1,x2))
subset(x = mydat, subset = x1 > 0 & x2 < 0, select = c(x2,x1))
order()
mydat[order(var1,var2),]
假设要随机化数据集中顺序。只需要从均匀分布生成一个向量,并根据该向量进行排序。
df[order(runif(nrow(df))),]
- `is.na()` 返回一个逻辑向量,如果数据集中任何变量缺失则为 TRUE,否则为 FALSE。
- `complete.cases()` 返回一个逻辑向量,如果所有情况都完整则为 TRUE,否则为 FALSE。
> table(complete.cases(df))
如果处理面板数据,则此主题很重要。 面板数据 可以存储在宽格式中,每个单位一个观测值,每个时间段一个变量;也可以存储在长格式中,每个单位和时间段一个观测值。`reshape()` 将数据集重塑为宽格式或长格式。
> country <- c("'Angola'","'UK'","'France'")
> gdp.1960 <- c(1,2,3)
> gdp.1970 <- c(2,4,6)
> mydat <- data.frame(country,gdp.1960,gdp.1970)
> mydat # wide format
country gdp.1960 gdp.1970
1 Angola 1 2
2 UK 2 4
3 France 3 6
> reshape( data = mydat, varying = list(2:3) , v.names = "gdp", direction = "long") # long format
country time gdp id
1.1 Angola 1 1 1
2.1 UK 1 2 2
3.1 France 1 3 3
1.2 Angola 2 2 1
2.2 UK 2 4 2
3.2 France 2 6 3
- `varying` 给出随时间变化的列的编号
- `v.names` 给出随时间变化的变量的前缀
- `direction` 给出方向,可以是“long”或“wide”。
- 另请参阅
有时我们需要在数据集中复制一些行。例如,如果我们想生成一个具有面板数据结构的假数据集。在这种情况下,我们将首先生成时间不变变量,然后将每行复制一个给定的标量以创建随时间变化的变量。
可以使用epicalc包中的expand()
函数(由于此包不再存在,因此在[1]中给出了扩展的选项)。这将使每行乘以给定数字。
N <- 1000
T <- 5
wide <- data.frame(id = 1:N,f = rnorm(N), rep = T)
library("epicalc")
long <- expand(wide,index.var = "rep")
long$time <- rep(1:T,N)
我们也可以使用自己动手解决方案或创建自己的函数。这个想法很简单。我们创建一个向量,该向量为每行提供应复制的次数(在以下示例中为dups)。然后我们使用rep()
函数创建一个向量,该向量根据我们的需要重复行号。最后一步创建了一个新的数据集,它根据所需模式重复行。
expand <- function(df,dups){
df$dups <- dups
pattern <- rep(1:nrow(df), times=df$dups)
df2 <- df[pattern,]
index <- function(x){
1:length(x)
}
df2$year <- unlist(tapply(df2$dups, df2$id, index))
df2$dups <- NULL
return(df2)
}
df <- data.frame(x = rnorm(3), id = 1:3)
dups = c(3,1,2)
expand(df,dups)
合并数据可能非常混乱,尤其是在多次合并的情况下。这是一个简单的例子
我们有一个描述作者的表格
> authors <- data.frame(
+ surname = I(c("Tukey", "Venables", "Tierney", "Ripley", "McNeil")),
+ nationality = c("US", "Australia", "US", "UK", "Australia"),
+ deceased = c("yes", rep("no", 4)))
> authors
surname nationality deceased
1 Tukey US yes
2 Venables Australia no
3 Tierney US no
4 Ripley UK no
5 McNeil Australia no
还有一个描述书籍的表格
> books <- data.frame(
+ name = I(c("Tukey", "Venables", "Tierney",
+ "Ripley", "Ripley", "McNeil", "R Core")),
+ title = c("Exploratory Data Analysis",
+ "Modern Applied Statistics ...",
+ "LISP-STAT",
+ "Spatial Statistics", "Stochastic Simulation",
+ "Interactive Data Analysis",
+ "An Introduction to R"),
+ other.author = c(NA, "Ripley", NA, NA, NA, NA,
+ "Venables & Smith"))
> books
name title other.author
1 Tukey Exploratory Data Analysis <NA>
2 Venables Modern Applied Statistics ... Ripley
3 Tierney LISP-STAT <NA>
4 Ripley Spatial Statistics <NA>
5 Ripley Stochastic Simulation <NA>
6 McNeil Interactive Data Analysis <NA>
7 R Core An Introduction to R Venables & Smith
我们想根据作者姓名(第一个数据集中为“surname”,第二个数据集中为“name”)合并“books”和“authors”表。我们使用merge()命令。我们指定第一个和第二个数据集的名称,然后通过by.x和by.y指定两个数据集中的标识符。all.x和all.y指定是否要保留第一个和第二个数据集的所有观察结果。在这种情况下,我们希望保留来自“books”数据集的所有观察结果,但我们只保留与“books”数据集中的观察结果匹配的来自“author”数据集的观察结果。
> final <- merge(books, authors, by.x = "name", by.y = "surname", sort=F,all.x=T,all.y=F)
> final
name title other.author nationality deceased
1 Tukey Exploratory Data Analysis <NA> US yes
2 Venables Modern Applied Statistics ... Ripley Australia no
3 Tierney LISP-STAT <NA> US no
4 Ripley Spatial Statistics <NA> UK no
5 Ripley Stochastic Simulation <NA> UK no
6 McNeil Interactive Data Analysis <NA> Australia no
7 R Core An Introduction to R Venables & Smith <NA> <NA>
也可以合并两个数据帧对象,同时保留两个合并对象之一的行顺序。[6]
- ↑ AER 包 https://cran.r-project.org.cn/web/packages/AER/index.html
- ↑ EcDat 包 https://cran.r-project.org.cn/web/packages/Ecdat/index.html
- ↑ “对英国贫困变化原因的调查,主要是在过去两个人口普查之间十年(第一部分)” - GU Yule - 英国皇家统计学会杂志,1899 年 6 月,第 283 页
- ↑ 使用 reshape 包重塑数据:http://www.jstatsoft.org/v21/i12
- ↑ tables 包的说明:https://cran.r-project.org.cn/web/packages/tables/vignettes/tables.pdf
- ↑ 合并数据帧同时保留行顺序
- ↑ R 数据手册 https://cran.r-project.org.cn/doc/manuals/R-data.html
- ↑ Paul Murrell 数据技术入门 http://www.stat.auckland.ac.nz/~paul/ItDT/