R'de vlookup ve doldurma (Excel'deki gibi) nasıl yapılır?


84

105000 satır ve 30 sütun ile ilgili bir veri kümem var. Bir sayıya atamak istediğim kategorik bir değişkenim var. Excel'de muhtemelen bir şeyler yapıp VLOOKUPdoldururdum.

Aynı şeyi içinde nasıl yapacağım R?

Esasen, sahip olduğum şey bir HouseTypedeğişkendir ve HouseTypeNo. İşte bazı örnek veriler:

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

Yanıtlar:


118

Sorunuzu doğru anladıysam, işte Excel'in eşdeğerini yapmak ve bunu VLOOKUPkullanarak doldurmak için dört yöntem vardır 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

İşte doldurmak için dört yöntem vardır HouseTypeNoiçinde largetabledeğerleri kullanılarak lookuptabloda:

İlk olarak mergeüssünde:

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

Tabanda adlandırılmış vektörlere sahip ikinci bir yöntem:

# 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]))

Üçüncüsü, plyrpaketi kullanarak :

# 3. using the plyr package
library(plyr)
plyr1 <- join(largetable, lookup, by = "HouseType")

Dördüncü olarak, sqldfpaketi kullanarak

# 4. using the sqldf package
library(sqldf)
sqldf1 <- sqldf("SELECT largetable.HouseType, lookup.HouseTypeNo
FROM largetable
INNER JOIN lookup
ON largetable.HouseType = lookup.HouseType")

Bazı ev türlerinin içinde largetablebulunmaması mümkünse, lookupbir sol birleştirme kullanılacaktır:

sqldf("select * from largetable left join lookup using (HouseType)")

Diğer çözümlere karşılık gelen değişikliklere de ihtiyaç duyulacaktır.

Yapmak istediğin bu muydu? Hangi yöntemi beğendiğinizi bana bildirin, ben de yorum ekleyeceğim.


1
Geç olduğunu fark ettim ama yardımın için teşekkürler. Hem birinci hem de ikinci yöntemi denedim. İkisi de iyi çalıştı. Soruyu yanıtladığınız için tekrar teşekkürler!
user2142810

1
Rica ederim. Sorunuzu cevapladıysa, sol üstteki okların altındaki tik işaretine tıklayarak bunu belirtebilirsiniz. Bu, aynı soruyu soranlara yardımcı olacaktır.
Ben

2
Bence çözüm # 2 yalnızca örneğinizde benzersiz değerler artan sırada olduğu için işe yarıyor (= ilk benzersiz ad 1, ikinci benzersiz ad 2 vb.). 'Hous' eklerseniz , ikinci satırda 'HousType = ECII', HousTypeNo = '17 'diyelim, arama tamamen yanlış gidiyor.
ECII

1
@ECII lütfen devam edin ve sorunu gösteren ve çözümünüzü gösteren cevabınızı ekleyin
Ben

1
Harika gönderi. Paylaşım için teşekkürler! # 4 uygulamam için iyi çalıştı ... iki çok büyük, 400MB masayı birleştirerek.
Nathaniel Payne

25

Ayrıca şunları da kullanabileceğinizi düşünüyorum match():

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

Sırasını karıştırırsam bu hala işe yarıyor lookup.


10

Ayrıca qdapTools::lookupikili operatörü kullanmayı veya kısaltmayı seviyorum %l%. Excel vlookup ile aynı şekilde çalışır, ancak sütun numaralarının aksine ad argümanlarını kabul eder.

## 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'in cevabının 2. Çözümü , diğer daha genel örneklerde tekrarlanamaz. Örnekte doğru aramayı verir çünkü benzersiz in artan HouseTypesırada housesgörünür. Bunu dene:

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 çözümü # 2 verir

housenames <- as.numeric(1:length(unique(hous$HouseType)))
names(housenames) <- unique(hous$HouseType)
base2 <- data.frame(HouseType = largetable$HouseType,
                    HouseTypeNo = (housenames[largetable$HouseType]))

Hangi zaman

unique(base2$HouseTypeNo[ base2$HouseType=="ECIIsHome" ])
[1] 2

arama tablosundan doğru cevap 17 olduğunda

Bunu yapmanın doğru yolu

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

Şimdi aramalar doğru bir şekilde gerçekleştiriliyor

unique(base2$HouseTypeNo[ base2$HouseType=="ECIIsHome" ])
ECIIsHome 
       17

Bens'in cevabını düzenlemeye çalıştım ama anlayamadığım nedenlerden dolayı reddedildi.


5

Şununla başlayarak:

houses <- read.table(text="Semi            1
Single          2
Row             3
Single          2
Apartment       4
Apartment       4
Row             3",col.names=c("HouseType","HouseTypeNo"))

... kullanabilirsiniz

as.numeric(factor(houses$HouseType))

... her ev tipi için benzersiz bir numara vermek. Sonucu burada görebilirsiniz:

> 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

... böylece satırlarda farklı sayılar elde edersiniz (çünkü faktörler alfabetik olarak sıralanmıştır), ancak aynı model.

(DÜZENLEME: bu cevapta kalan metin aslında gereksiz. Kontrol etmek read.table()için aklıma geldi ve ilk etapta veri çerçevesine okunduğunda evleri $ HouseType bir faktöre dönüştürmüş olduğu ortaya çıktı).

Bununla birlikte, sadece HouseType'ı bir faktöre dönüştürmek daha iyi olabilir, bu da size HouseTypeNo ile aynı faydaları sağlar, ancak ev türleri numaralandırılmak yerine adlandırıldığı için yorumlanması daha kolay olur, örneğin:

> 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

Poster, değerlere bakmayı sormadı exact=FALSE, ancak bunu kendi referansım ve muhtemelen başkaları için bir cevap olarak ekliyorum.

Kategorik değerler arıyorsanız, diğer cevapları kullanın.

Excel'in vlookupayrıca sayısal değerleri yaklaşık olarak 4. bağımsız değişkenle (1) eşleştirmenize izin verir match=TRUE. match=TRUEBir termometrede değerlere bakmayı düşünüyorum . Varsayılan değer YANLIŞ'tır ve kategorik değerler için mükemmeldir.

Yaklaşık olarak eşleştirmek istiyorsanız (bir arama yapın), R'nin findInterval(adından da anlaşılacağı gibi) sürekli sayısal değerinizi içeren aralığı / bin'i bulacak adlı bir işlevi vardır .

Ancak, findIntervalbirkaç değer için istediğinizi varsayalım. Bir döngü yazabilir veya uygulama işlevi kullanabilirsiniz. Bununla birlikte, DIY vektörleştirilmiş bir yaklaşım benimsemeyi daha verimli buldum.

Diyelim ki x ve y tarafından indekslenmiş bir değerler ızgaranız var:

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

ve x ve y'ye göre aramak istediğiniz bazı değerlere sahipsiniz:

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

İşte görselleştirilmiş örnek:

contour(grid)
points(df$x, df$y, pch=df$id, col="blue", cex=1.2)

Kontur Grafiği

Bu tür formülle x aralıklarını ve y aralıklarını bulabilirsiniz:

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

Bunu bir adım daha ileri götürebilir ve aşağıdaki gridgibi z değerleri üzerinde (basit) bir enterpolasyon yapabilirsiniz :

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)

Bu size şu değerleri verir:

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)

Değerli kontur grafiği

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

İx ve iy'in bir döngü kullanılarak da bulunabileceğine dikkat edin findInterval, örneğin burada ikinci satır için bir örnek var

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

Hangi maçlarda ixve iyiçindedf[2]

Dipnot: (1) vlookup'ın dördüncü argümanı daha önce "eşleşme" olarak adlandırılıyordu, ancak şeridi tanıttıktan sonra "[aralık_bak]" olarak yeniden adlandırıldı.


4

mapvalues()Plyr paketinden kullanabilirsiniz .

İlk veri:

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

Arama / yaya geçidi tablosu:

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

Yeni değişkeni oluşturun:

dat$house_type_num <- plyr::mapvalues(dat$HouseType, from = lookup$type_text, to = lookup$type_num)

Veya basit değiştirmeler için uzun bir arama tablosu oluşturmayı atlayabilir ve bunu doğrudan tek adımda yapabilirsiniz:

dat$house_type_num <- plyr::mapvalues(dat$HouseType,
                                      from = c("Semi", "Single", "Row", "Apartment"),
                                      to = c(1, 2, 3, 4))

Sonuç:

> 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

Kullanım merge, arama tablosunda birincil anahtar kısıtlaması uygulanmazsa verilerinizi çoğaltma (çarpma) veya kullanmıyorsanız kayıt sayısını azaltma potansiyeline sahip olduğundan, Excel'de aramadan farklıdır all.x = T.

Bununla başınızı belaya sokmadığınızdan ve güvenli bir şekilde arama yaptığınızdan emin olmak için iki strateji öneriyorum.

Birincisi, arama anahtarında bir dizi yinelenen satırı kontrol etmektir:

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

Bu, sizi kullanmadan önce arama veri kümesini tekilleştirmeye zorlar:

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

İkinci seçenek, arama veri kümesinden ilk eşleşen değeri alarak Excel davranışını yeniden oluşturmaktır:

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

Bu işlevler, lookupbirden çok sütun eklediklerinden biraz farklıdır .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.