2013-03-08 66 views
58

我有一個關於105000行和30列的數據集。我有一個分類變量,我想將其分配給一個數字。在Excel中,我可能會用VLOOKUP做一些事情並填寫。如何在R中查找並填充(如在Excel中)?

我該如何去做同樣的事情在R

本質上,我有一個HouseType變量,我需要計算HouseTypeNo。下面是一些樣本數據:

HouseType HouseTypeNo 
Semi   1 
Single   2 
Row    3 
Single   2 
Apartment  4 
Apartment  4 
Row    3 

回答

89

如果我正確理解你的問題,這裏有四種方法做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 

這裏有四種方法來填補HouseTypeNolargetable利用在lookup表中的值:

先用merge在鹼:

# 1. using base 
base1 <- (merge(lookup, largetable, by = 'HouseType')) 

與名爲矢量的第二方法在鹼:

# 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

我意識到這是相當晚,但感謝您的幫助。我嘗試了第一種和第二種方法。他們兩人都工作得很好。再次感謝您回答這個問題! – user2142810 2013-04-04 21:22:38

+0

不客氣。如果它回答了您的問題,您可以通過點擊左上方箭頭下方的勾號來指明。這對有相同問題的人有幫助。 – Ben 2013-04-04 21:50:32

+2

我認爲解決方案#2的工作原理僅僅是因爲在你的例子中,唯一的值恰巧是遞增順序的(=第一個唯一名稱是1,第二個唯一名稱是2等等)。如果你在第二行*'HousType = ECII'中添加'hous',HousTypeNo = '17',則查找全部錯誤。 – ECII 2013-12-09 18:43:20

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" 
9

我也很喜歡使用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 
6

解決方案#2的@本的答案是不是在其他更通用的例子可重複的名稱參數。它恰好在示例中給出了正確的查找,因爲houses中的唯一HouseType以升序顯示。試試這個:

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答案,但它被拒絕的原因,我無法理解。

11

我想你也可以使用match()

largetable$HouseTypeNo <- with(lookup, 
        HouseTypeNo[match(largetable$HouseType, 
             HouseType)]) 

,如果我搶的lookup順序這仍然有效。

3

您可以使用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 
4

的海報並沒有問,如果exact=FALSE查找值,但是我將其添加爲我自己的參考和其他可能的答案。

如果您正在查找分類值,請使用其他答案。

Excel的vlookup也允許您匹配數值大致匹配與第四個參數(1)match=TRUE。我想到match=TRUE就像在溫度計上查看數值。默認值爲FALSE,對於分類值來說是完美的。

如果要近似匹配(執行查找),R有一個名爲findInterval的函數,該函數(顧名思義)將查找包含連續數值的間隔/ bin。

但是,假設您想爲findInterval取幾個值。你可以寫一個循環或使用一個應用函數。不過,我發現採用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) 

Contour Plot

,可以看到在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) 

Contour plot with values

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 

請注意,也可以使用findInterval例如findInterval找到ix和iy。這裏是第二行

findInterval(df$x[2], grid$x) 
# 4 
findInterval(df$y[2], grid$y) 
# 2 

其中在比賽ixiy一個例子df[2]

腳註: (1)VLOOKUP的第四個參數以前被稱爲「匹配」,但之後他們推出了緞帶被重新命名爲「[range_lookup]」。

2

使用merge與Excel中的查找不同,因爲如果在查找表中未實施主鍵約束,或者如果您未使用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) 
} 

這將迫使你使用它之前的重複數據刪除查找數據集:

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 
# ... 

二選項是通過從查找數據集中獲取第一個匹配值來重現Excel行爲:

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") 

這些函數與lookup稍有不同,因爲它們添加了多列。