R में vlookup और एक्सेल (जैसे एक्सेल) कैसे भरें?


84

मेरे पास लगभग 105000 पंक्तियाँ और 30 कॉलम हैं। मेरे पास एक श्रेणीगत चर है जिसे मैं इसे एक संख्या में निर्दिष्ट करना चाहूंगा। एक्सेल में, मैं शायद कुछ करूंगा VLOOKUPऔर भरूंगा।

मैं उसी काम को कैसे करूंगा R?

अनिवार्य रूप से, मेरे पास एक HouseTypeचर है, और मुझे गणना करने की आवश्यकता है HouseTypeNo। यहाँ कुछ नमूना डेटा हैं:

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

जवाबों:


118

यदि मैं आपके प्रश्न को सही ढंग से समझता हूं, तो यहां एक्सेल के बराबर करने के लिए चार तरीके दिए गए हैं 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

1
आपका स्वागत है। यदि यह आपके प्रश्न का उत्तर देता है तो आप तीर के नीचे ऊपरी बाईं ओर टिक पर क्लिक करके इसे इंगित कर सकते हैं। जो दूसरों के लिए एक ही सवाल है, वह मददगार होगा।
बेन

2
मुझे लगता है कि समाधान # 2 केवल इसलिए काम करता है क्योंकि आपके उदाहरण में अद्वितीय मूल्य बढ़ते क्रम में होते हैं (= पहला अद्वितीय नाम 1 है दूसरा अद्वितीय नाम 2 है और इसी तरह)। यदि आप 'हाउस' में जोड़ते हैं तो दूसरी पंक्ति में कहते हैं 'HousType = ECII', HousTypeNo = '17 'लुकअप सभी गलत हो जाता है।
ईसीआई

1
@ECII कृपया आगे बढ़ें और अपना उत्तर जोड़ें जो समस्या का चित्रण करता है और आपका समाधान दिखाता है
बेन

1
महान पद। साझा करने के लिए धन्यवाद! # 4 मेरे आवेदन के लिए अच्छी तरह से काम किया ... दो बहुत बड़े, 400MB तालिकाओं में शामिल होना।
नाथनियल पायने

25

मुझे लगता है कि आप भी उपयोग कर सकते हैं match():

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

यह अभी भी काम करता है अगर मैं के आदेश हाथापाई lookup


10

मैं भी qdapTools::lookupबाइनरी ऑपरेटर का उपयोग या शॉर्टहैंड करना पसंद करता हूं %l%। यह एक्सेल वूडअप के लिए समान रूप से काम करता है, लेकिन यह स्तंभ संख्याओं के विपरीत नाम के तर्कों को स्वीकार करता है

## 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 अन्य अधिक सामान्य उदाहरणों में प्रतिलिपि प्रस्तुत करने योग्य नहीं है। यह उदाहरण में सही लुक देने के लिए होता है क्योंकि अद्वितीय बढ़ते क्रम 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 उत्तर को संपादित करने की कोशिश की, लेकिन यह उन कारणों के लिए अस्वीकार हो जाता है, जिन्हें मैं नहीं समझ सकता।


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

... इसलिए आप पंक्तियों पर अलग-अलग संख्याओं के साथ समाप्त होते हैं (क्योंकि कारक वर्णानुक्रम में आदेश दिए गए हैं) लेकिन एक ही पैटर्न।

(EDIT: इस उत्तर में बचा हुआ पाठ वास्तव में निरर्थक है। यह मुझे जांचने के लिए हुआ और यह पता चला read.table() पहले से ही मकान में $ HouseType को एक कारक बना दिया गया था जब इसे पहली बार डेटाफ़्रेम में पढ़ा गया था)।

हालाँकि, आप अच्छी तरह से बेहतर हो सकते हैं कि हाउसटाइप को एक फैक्टर में बदल दें, जिससे आपको हाउसटाइप नो के समान सभी लाभ मिलेंगे, लेकिन व्याख्या करना आसान होगा क्योंकि घर के प्रकार गिने के बजाय नामित किए जाते हैं, जैसे:

> 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 , लेकिन मैं इसे अपने स्वयं के संदर्भ के लिए एक उत्तर के रूप में जोड़ रहा हूं और संभवतः अन्य।

यदि आप श्रेणीबद्ध मूल्यों को देख रहे हैं, तो अन्य उत्तरों का उपयोग करें।

एक्सेल vlookupभी आपको 4 वें तर्क (1) के साथ संख्यात्मक मूल्यों के लिए लगभग मैच करने की अनुमति देता है match=TRUE। मैं सोचता हूं बारे मेंmatch=TRUE थर्मामीटर पर मूल्यों को देखने तरह । डिफ़ॉल्ट मान FALSE है, जो श्रेणीबद्ध मूल्यों के लिए एकदम सही है।

यदि आप लगभग मिलान करना चाहते हैं (एक लुकअप प्रदर्शन करते हैं), आर में एक फ़ंक्शन है 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

जो मेल खाता है ixऔर iyमेंdf[2]

फुटनोट: (1) वेनसुप के चौथे तर्क को पहले "मैच" कहा जाता था, लेकिन उन्होंने रिबन पेश करने के बाद इसका नाम बदलकर "[range_lookup]" कर दिया।


4

आप 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

3

mergeएक्सेल में लुकअप का उपयोग करना अलग है क्योंकि इसमें आपके डेटा की डुप्लिकेट (गुणा) करने की क्षमता है यदि प्राथमिक कुंजी बाधा लुकअप तालिका में लागू नहीं है या यदि आप उपयोग नहीं कर रहे हैं तो रिकॉर्ड की संख्या कम करें 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
# ...

दूसरा विकल्प लुकअप डेटासेट से पहला मिलान मूल्य लेकर एक्सेल व्यवहार को पुन: पेश करना है:

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कई कॉलमों को जोड़ने से थोड़े अलग हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.