我有一个大约105000行和30列的数据集。我有一个分类变量,我想将它分配给一个数字。在Excel中,我可能会使用VLOOKUP
并填充。
我如何在R
中做同样的事情?
基本上,我所拥有的是HouseType
变量,我需要计算HouseTypeNo
。以下是一些示例数据:
HouseType HouseTypeNo
Semi 1
Single 2
Row 3
Single 2
Apartment 4
Apartment 4
Row 3
答案 0 :(得分:101)
如果我正确理解您的问题,可以使用以下四种方法来执行相同的Excel VLOOKUP
并使用R
填写:
# load sample data from Q
hous <- read.table(header = TRUE,
stringsAsFactors = FALSE,
text="HouseType HouseTypeNo
Semi 1
Single 2
Row 3
Single 2
Apartment 4
Apartment 4
Row 3")
# create a toy large table with a 'HouseType' column
# but no 'HouseTypeNo' column (yet)
largetable <- data.frame(HouseType = as.character(sample(unique(hous$HouseType), 1000, replace = TRUE)), stringsAsFactors = FALSE)
# create a lookup table to get the numbers to fill
# the large table
lookup <- unique(hous)
HouseType HouseTypeNo
1 Semi 1
2 Single 2
3 Row 3
5 Apartment 4
以下四种方法使用HouseTypeNo
表中的值填充largetable
中的lookup
:
首先使用基数merge
:
# 1. using base
base1 <- (merge(lookup, largetable, by = 'HouseType'))
在base中使用命名向量的第二种方法:
# 2. using base and a named vector
housenames <- as.numeric(1:length(unique(hous$HouseType)))
names(housenames) <- unique(hous$HouseType)
base2 <- data.frame(HouseType = largetable$HouseType,
HouseTypeNo = (housenames[largetable$HouseType]))
第三,使用plyr
包:
# 3. using the plyr package
library(plyr)
plyr1 <- join(largetable, lookup, by = "HouseType")
第四,使用sqldf
包
# 4. using the sqldf package
library(sqldf)
sqldf1 <- sqldf("SELECT largetable.HouseType, lookup.HouseTypeNo
FROM largetable
INNER JOIN lookup
ON largetable.HouseType = lookup.HouseType")
如果largetable
中lookup
中的某些房屋类型可能不存在,则会使用左连接:
sqldf("select * from largetable left join lookup using (HouseType)")
也需要对其他解决方案进行相应的更改。
那是你想做的吗?让我知道您喜欢哪种方法,我会添加评论。
答案 1 :(得分:16)
我认为你也可以使用match()
:
largetable$HouseTypeNo <- with(lookup,
HouseTypeNo[match(largetable$HouseType,
HouseType)])
如果我扰乱lookup
的顺序,这仍然有用。
答案 2 :(得分:10)
我也喜欢使用qdapTools::lookup
或速记二元运算符%l%
。它与Excel vlookup的工作方式相同,但它接受与列号相对的名称参数
## Replicate Ben's data:
hous <- structure(list(HouseType = c("Semi", "Single", "Row", "Single",
"Apartment", "Apartment", "Row"), HouseTypeNo = c(1L, 2L, 3L,
2L, 4L, 4L, 3L)), .Names = c("HouseType", "HouseTypeNo"),
class = "data.frame", row.names = c(NA, -7L))
largetable <- data.frame(HouseType = as.character(sample(unique(hous$HouseType),
1000, replace = TRUE)), stringsAsFactors = FALSE)
## It's this simple:
library(qdapTools)
largetable[, 1] %l% hous
答案 3 :(得分:6)
HouseType
中的唯一houses
按递增顺序出现。试试这个:
hous <- read.table(header = TRUE, stringsAsFactors = FALSE, text="HouseType HouseTypeNo
Semi 1
ECIIsHome 17
Single 2
Row 3
Single 2
Apartment 4
Apartment 4
Row 3")
largetable <- data.frame(HouseType = as.character(sample(unique(hous$HouseType), 1000, replace = TRUE)), stringsAsFactors = FALSE)
lookup <- unique(hous)
Bens解决方案#2给出了
housenames <- as.numeric(1:length(unique(hous$HouseType)))
names(housenames) <- unique(hous$HouseType)
base2 <- data.frame(HouseType = largetable$HouseType,
HouseTypeNo = (housenames[largetable$HouseType]))
当
unique(base2$HouseTypeNo[ base2$HouseType=="ECIIsHome" ])
[1] 2
当查找表中的正确答案是17时
正确的方法是
hous <- read.table(header = TRUE, stringsAsFactors = FALSE, text="HouseType HouseTypeNo
Semi 1
ECIIsHome 17
Single 2
Row 3
Single 2
Apartment 4
Apartment 4
Row 3")
largetable <- data.frame(HouseType = as.character(sample(unique(hous$HouseType), 1000, replace = TRUE)), stringsAsFactors = FALSE)
housenames <- tapply(hous$HouseTypeNo, hous$HouseType, unique)
base2 <- data.frame(HouseType = largetable$HouseType,
HouseTypeNo = (housenames[largetable$HouseType]))
现在正确执行查找
unique(base2$HouseTypeNo[ base2$HouseType=="ECIIsHome" ])
ECIIsHome
17
我尝试编辑Bens的答案,但由于我无法理解的原因而被拒绝。
答案 4 :(得分:5)
从:
开始houses <- read.table(text="Semi 1
Single 2
Row 3
Single 2
Apartment 4
Apartment 4
Row 3",col.names=c("HouseType","HouseTypeNo"))
......你可以使用
as.numeric(factor(houses$HouseType))
...为每种房屋类型提供唯一编号。你可以在这里看到结果:
> houses2 <- data.frame(houses,as.numeric(factor(houses$HouseType)))
> houses2
HouseType HouseTypeNo as.numeric.factor.houses.HouseType..
1 Semi 1 3
2 Single 2 4
3 Row 3 2
4 Single 2 4
5 Apartment 4 1
6 Apartment 4 1
7 Row 3 2
...所以你最终会在行上使用不同的数字(因为这些因素是按字母顺序排列的)但是相同的模式。
(编辑:这个答案中的其余文字实际上是多余的。我想到要检查一下,事实证明read.table()
已将房屋$ HouseType作为一个因素读入数据框中第一名)。
但是,将HouseType转换为一个因子可能会更好,这会给你带来与HouseTypeNo相同的好处,但会更容易解释,因为房屋类型是命名而不是编号,例如:
> houses3 <- houses
> houses3$HouseType <- factor(houses3$HouseType)
> houses3
HouseType HouseTypeNo
1 Semi 1
2 Single 2
3 Row 3
4 Single 2
5 Apartment 4
6 Apartment 4
7 Row 3
> levels(houses3$HouseType)
[1] "Apartment" "Row" "Semi" "Single"
答案 5 :(得分:5)
海报并未询问如果exact=FALSE
查找值,但我将此作为我自己参考的答案,可能还有其他人。
如果您要查找分类值,请使用其他答案。
Excel的vlookup
还允许您将数字值的匹配与第4个参数(1)match=TRUE
匹配。我认为match=TRUE
喜欢在温度计上查找值。默认值为FALSE,非常适合分类值。
如果你想匹配近似(执行查找),R有一个名为findInterval
的函数,(顾名思义)将找到包含连续数值的区间/ bin。
但是,假设您希望findInterval
获得多个值。您可以编写循环或使用apply函数。但是,我发现采用DIY矢量化方法更有效率。
假设您有一个由x和y索引的值网格:
grid <- list(x = c(-87.727, -87.723, -87.719, -87.715, -87.711),
y = c(41.836, 41.839, 41.843, 41.847, 41.851),
z = (matrix(data = c(-3.428, -3.722, -3.061, -2.554, -2.362,
-3.034, -3.925, -3.639, -3.357, -3.283,
-0.152, -1.688, -2.765, -3.084, -2.742,
1.973, 1.193, -0.354, -1.682, -1.803,
0.998, 2.863, 3.224, 1.541, -0.044),
nrow = 5, ncol = 5)))
你想要通过x和y查找一些值:
df <- data.frame(x = c(-87.723, -87.712, -87.726, -87.719, -87.722, -87.722),
y = c(41.84, 41.842, 41.844, 41.849, 41.838, 41.842),
id = c("a", "b", "c", "d", "e", "f")
以下是可视化的示例:
contour(grid)
points(df$x, df$y, pch=df$id, col="blue", cex=1.2)
您可以使用以下公式找到x间隔和y间隔:
xrng <- range(grid$x)
xbins <- length(grid$x) -1
yrng <- range(grid$y)
ybins <- length(grid$y) -1
df$ix <- trunc( (df$x - min(xrng)) / diff(xrng) * (xbins)) + 1
df$iy <- trunc( (df$y - min(yrng)) / diff(yrng) * (ybins)) + 1
您可以更进一步,对grid
中的z值执行(简单化)插值,如下所示:
df$z <- with(df, (grid$z[cbind(ix, iy)] +
grid$z[cbind(ix + 1, iy)] +
grid$z[cbind(ix, iy + 1)] +
grid$z[cbind(ix + 1, iy + 1)]) / 4)
它为您提供了以下值:
contour(grid, xlim = range(c(grid$x, df$x)), ylim = range(c(grid$y, df$y)))
points(df$x, df$y, pch=df$id, col="blue", cex=1.2)
text(df$x + .001, df$y, lab=round(df$z, 2), col="blue", cex=1)
df
# x y id ix iy z
# 1 -87.723 41.840 a 2 2 -3.00425
# 2 -87.712 41.842 b 4 2 -3.11650
# 3 -87.726 41.844 c 1 3 0.33150
# 4 -87.719 41.849 d 3 4 0.68225
# 6 -87.722 41.838 e 2 1 -3.58675
# 7 -87.722 41.842 f 2 2 -3.00425
请注意,ix和iy也可能在使用findInterval
的循环中找到,例如这是第二行的一个例子
findInterval(df$x[2], grid$x)
# 4
findInterval(df$y[2], grid$y)
# 2
哪个匹配ix
iy
和df[2]
脚注: (1)vlookup的第四个参数以前称为“匹配”,但在引入功能区后,它被重命名为“[range_lookup]”。
答案 6 :(得分:4)
您可以使用plyr包中的mapvalues()
。
初始数据:
dat <- data.frame(HouseType = c("Semi", "Single", "Row", "Single", "Apartment", "Apartment", "Row"))
> dat
HouseType
1 Semi
2 Single
3 Row
4 Single
5 Apartment
6 Apartment
7 Row
查询/人行横道表:
lookup <- data.frame(type_text = c("Semi", "Single", "Row", "Apartment"), type_num = c(1, 2, 3, 4))
> lookup
type_text type_num
1 Semi 1
2 Single 2
3 Row 3
4 Apartment 4
创建新变量:
dat$house_type_num <- plyr::mapvalues(dat$HouseType, from = lookup$type_text, to = lookup$type_num)
或者对于简单的替换,您可以跳过创建长查找表并直接一步完成:
dat$house_type_num <- plyr::mapvalues(dat$HouseType,
from = c("Semi", "Single", "Row", "Apartment"),
to = c(1, 2, 3, 4))
结果:
> dat
HouseType house_type_num
1 Semi 1
2 Single 2
3 Row 3
4 Single 2
5 Apartment 4
6 Apartment 4
7 Row 3
答案 7 :(得分:3)
使用merge
与Excel中的查找不同,因为如果未在查找表中强制执行主键约束,则可能会复制(相乘)数据;如果不使用{{1>,则减少记录数量}}
为了确保您不会遇到麻烦并安全查找,我建议采用两种策略。
首先要检查查找键中的多个重复行:
all.x = T
这会强制您在使用之前对查找数据集进行重复数据删除:
safeLookup <- function(data, lookup, by, select = setdiff(colnames(lookup), by)) {
# Merges data to lookup making sure that the number of rows does not change.
stopifnot(sum(duplicated(lookup[, by])) == 0)
res <- merge(data, lookup[, c(by, select)], by = by, all.x = T)
return (res)
}
第二个选项是通过从查找数据集中获取第一个匹配值来重现Excel行为:
baseSafe <- safeLookup(largetable, house.ids, by = "HouseType")
# Error: sum(duplicated(lookup[, by])) == 0 is not TRUE
baseSafe<- safeLookup(largetable, unique(house.ids), by = "HouseType")
head(baseSafe)
# HouseType HouseTypeNo
# 1 Apartment 4
# 2 Apartment 4
# ...
这些功能与firstLookup <- function(data, lookup, by, select = setdiff(colnames(lookup), by)) {
# Merges data to lookup using first row per unique combination in by.
unique.lookup <- lookup[!duplicated(lookup[, by]), ]
res <- merge(data, unique.lookup[, c(by, select)], by = by, all.x = T)
return (res)
}
baseFirst <- firstLookup(largetable, house.ids, by = "HouseType")
略有不同,因为它们会添加多列。
答案 8 :(得分:0)
可以在此处使用 lookup
包:
library(lookup)
# reference data
hous <- data.frame(HouseType=c("Semi","Single","Row","Single","Apartment","Apartment","Row"),
HouseTypeNo=c(1,2,3,2,4,4,3))
# new large data with HouseType but no HouseTypeNo
largetable <- data.frame(HouseType = sample(unique(hous$HouseType), 1000, replace = TRUE))
# vector approach
largetable$num1 <- lookup(largetable$HouseType, hous$HouseType, hous$HouseTypeNo)
# dataframe approach
largetable$num2 <- vlookup(largetable$HouseType, hous, "HouseType", "HouseTypeNo")
head(largetable)
# HouseType num1 num2
# 1 Semi 1 1
# 2 Semi 1 1
# 3 Apartment 4 4
# 4 Semi 1 1
# 5 Single 2 2
# 6 Single 2 2