R에서 vlookup을 수행하고 Excel에서와 같이 채우는 방법은 무엇입니까?


82

약 105000 행과 30 열에 대한 데이터 세트가 있습니다. 숫자에 할당하고 싶은 범주 형 변수가 있습니다. Excel에서는 아마도 뭔가를 VLOOKUP하고 채울 것입니다.

에서 똑같은 일을 R어떻게할까요?

본질적으로 내가 가진 것은 HouseType변수이며 HouseTypeNo. 다음은 몇 가지 샘플 데이터입니다.

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

답변:


116

귀하의 질문을 올바르게 이해했다면 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'))

베이스에 명명 된 벡터를 사용하는 두 번째 방법 :

# 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

1
천만에요. 질문에 대한 답변이 있으면 왼쪽 상단의 화살표 아래에있는 체크 표시를 클릭하여이를 표시 할 수 있습니다. 그것은 같은 질문을 가진 다른 사람들에게 도움이 될 것입니다.
Ben

2
솔루션 # 2는 귀하의 예에서 고유 값이 증가하는 순서로 발생하기 때문에 작동한다고 생각합니다 (= 첫 번째 고유 이름은 1이고 두 번째 고유 이름은 2입니다). 'hous'를 추가하면 두 번째 행에서 'HousType = ECII', HousTypeNo = '17 '조회가 모두 잘못됩니다.
ECII

1
@ECII 계속해서 문제를 설명하고 해결책을 보여주는 답을 추가하십시오
Ben

1
좋은 게시물입니다. 공유해 주셔서 감사합니다! # 4는 내 응용 프로그램에서 훌륭하게 작동했습니다 ... 두 개의 매우 큰 400MB 테이블을 결합했습니다.
Nathaniel Payne

25

다음을 사용할 수도 있다고 생각합니다 match().

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

나는 순서를 뒤섞어도 여전히 작동합니다 lookup.


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

6

@Ben의 답변 중 솔루션 # 2 는 다른 일반적인 예에서 재현 할 수 없습니다. 고유 항목 이 오름차순 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)

벤스 솔루션 # 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 답변을 편집하려고했지만 이해할 수없는 이유로 거부되었습니다.


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()했으며 처음에 데이터 프레임으로 읽었을 때 이미 house $ 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

포스터는 값을 찾는 것에 대해 묻지 exact=FALSE않았지만 내 자신과 다른 사람들에 대한 답변으로 이것을 추가하고 있습니다.

범주 형 값을 찾는 경우 다른 답변을 사용하십시오.

Excel vlookup에서는 4 번째 인수 (1)를 사용하여 숫자 값에 대해 대략적으로 일치시킬 수 있습니다 match=TRUE. 내 생각 엔match=TRUE 온도계에서 값을 찾는 것과 같다고 합니다. 기본값은 FALSE이며 범주 형 값에 적합합니다.

대략적으로 일치시키려는 경우 (조회 수행) R에는이라는 함수가 있으며 findInterval, 이는 이름에서 알 수 있듯이 연속 숫자 값을 포함하는 간격 / 빈을 찾습니다.

그러나 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)

등고선 플롯

다음 유형의 공식을 사용하여 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

어떤 경기 ixiydf[2]

각주 : (1) vlookup의 네 번째 인수는 이전에 "일치"라고 불렸지만 리본을 도입 한 후에는 "[range_lookup]"으로 이름이 변경되었습니다.


4

mapvalues()plyr 패키지에서 사용할 수 있습니다 .

초기 데이터 :

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

3

을 사용하는 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여러 열을 추가하는 것과 약간 다릅니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.