Jeśli dobrze rozumiem twoje pytanie, oto cztery metody, aby zrobić odpowiednik Excela VLOOKUP
i wypełnić za pomocą R
:
hous <- read.table(header = TRUE,
stringsAsFactors = FALSE,
text="HouseType HouseTypeNo
Semi 1
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)
HouseType HouseTypeNo
1 Semi 1
2 Single 2
3 Row 3
5 Apartment 4
Oto cztery metody wypełniania pól HouseTypeNo
przy largetable
użyciu wartości z lookup
tabeli:
Najpierw merge
w bazie:
base1 <- (merge(lookup, largetable, by = 'HouseType'))
Druga metoda z nazwanymi wektorami w bazie:
housenames <- as.numeric(1:length(unique(hous$HouseType)))
names(housenames) <- unique(hous$HouseType)
base2 <- data.frame(HouseType = largetable$HouseType,
HouseTypeNo = (housenames[largetable$HouseType]))
Po trzecie, korzystając z plyr
pakietu:
library(plyr)
plyr1 <- join(largetable, lookup, by = "HouseType")
Po czwarte, korzystając z sqldf
pakietu
library(sqldf)
sqldf1 <- sqldf("SELECT largetable.HouseType, lookup.HouseTypeNo
FROM largetable
INNER JOIN lookup
ON largetable.HouseType = lookup.HouseType")
Jeśli jest możliwe, że niektóre typy domów largetable
nie istnieją w lookup
, zostanie użyte połączenie lewe:
sqldf("select * from largetable left join lookup using (HouseType)")
Potrzebne byłyby również odpowiednie zmiany w innych rozwiązaniach.
Czy to właśnie chciałeś zrobić? Daj mi znać, którą metodę lubisz, a dodam komentarz.
Myślę, że możesz również użyć
match()
:To nadal działa, jeśli pomieszam kolejność
lookup
.źródło
Lubię też używać
qdapTools::lookup
lub skróconego operatora binarnego%l%
. Działa tak samo jak przeglądarka Excela, ale akceptuje argumenty nazw w przeciwieństwie do numerów kolumn## 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
źródło
Rozwiązanie nr 2 odpowiedzi @ Bena nie jest możliwe do odtworzenia w innych, bardziej ogólnych przykładach. Zdarza się, że w tym przykładzie wyszukiwanie jest poprawne, ponieważ unikatowe
HouseType
whouses
pojawiają się w kolejności rosnącej. Spróbuj tego: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)
Rozwiązanie Bensa nr 2 daje
housenames <- as.numeric(1:length(unique(hous$HouseType))) names(housenames) <- unique(hous$HouseType) base2 <- data.frame(HouseType = largetable$HouseType, HouseTypeNo = (housenames[largetable$HouseType]))
które kiedy
unique(base2$HouseTypeNo[ base2$HouseType=="ECIIsHome" ]) [1] 2
kiedy prawidłowa odpowiedź to 17 w tabeli przeglądowej
Właściwy sposób to zrobić
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]))
Teraz wyszukiwania są wykonywane poprawnie
unique(base2$HouseTypeNo[ base2$HouseType=="ECIIsHome" ]) ECIIsHome 17
Próbowałem edytować odpowiedź Bensa, ale została odrzucona z powodów, których nie rozumiem.
źródło
Począwszy od:
houses <- read.table(text="Semi 1 Single 2 Row 3 Single 2 Apartment 4 Apartment 4 Row 3",col.names=c("HouseType","HouseTypeNo"))
... możesz użyć
as.numeric(factor(houses$HouseType))
... aby nadać unikalny numer każdemu typowi domu. Możesz zobaczyć wynik tutaj:
> 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
... więc otrzymujesz różne liczby w wierszach (ponieważ czynniki są uporządkowane alfabetycznie), ale ten sam wzór.
(EDYCJA: pozostały tekst w tej odpowiedzi jest właściwie zbędny. Przyszło mi do głowy, aby sprawdzić i okazało się, że
read.table()
domy $ HouseType stały się już czynnikiem, gdy był wczytywany do ramki danych w pierwszej kolejności).Jednak może być lepiej po prostu przekonwertować HouseType na współczynnik, który dałby wszystkie te same korzyści co HouseTypeNo, ale byłby łatwiejszy do zinterpretowania, ponieważ typy domów są nazwane, a nie numerowane, np .:
> 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"
źródło
Plakat nie zawierał pytania o szukanie wartości, jeśli
exact=FALSE
, ale dodam to jako odpowiedź dla siebie i być może dla innych.Jeśli szukasz wartości kategorycznych, użyj innych odpowiedzi.
W programie Excel można
vlookup
również dopasować w przybliżeniu wartości liczbowe do czwartego argumentu (1)match=TRUE
. Myślę omatch=TRUE
sprawdzaniu wartości na termometrze. Wartością domyślną jest FALSE, co jest idealne dla wartości jakościowych.Jeśli chcesz dopasować w przybliżeniu (przeprowadzić wyszukiwanie), R ma funkcję o nazwie
findInterval
, która (jak sama nazwa wskazuje) znajdzie przedział / przedział, który zawiera ciągłą wartość liczbową.Jednak powiedzmy, że chcesz
findInterval
dla kilku wartości. Możesz napisać pętlę lub użyć funkcji stosującej. Jednak wydaje mi się, że bardziej wydajne jest podejście wektoryzowane DIY.Powiedzmy, że masz siatkę wartości indeksowanych przez x i 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)))
i masz pewne wartości, które chcesz sprawdzić według x i 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")
Oto zwizualizowany przykład:
contour(grid) points(df$x, df$y, pch=df$id, col="blue", cex=1.2)
Możesz znaleźć odstępy x i odstępy y za pomocą tego typu formuł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
Możesz pójść o krok dalej i wykonać (uproszczoną) interpolację wartości z w
grid
następujący sposób: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)
Który daje te wartości:
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
Zwróć uwagę, że ix i iy można było również znaleźć za pomocą pętli przy użyciu
findInterval
, np. Tutaj jest jeden przykład dla drugiego wierszafindInterval(df$x[2], grid$x) # 4 findInterval(df$y[2], grid$y) # 2
Które mecze
ix
iiy
wdf[2]
Przypis: (1) Czwarty argument funkcji vlookup był wcześniej nazywany „dopasowaniem”, ale po wprowadzeniu wstążki została zmieniona na „[wyszukiwanie_zakresu]”.
źródło
Możesz użyć
mapvalues()
z pakietu plyr.Wstępne dane:
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
Tabela wyszukiwania / przejścia dla pieszych:
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
Utwórz nową zmienną:
Lub w przypadku prostych zamian możesz pominąć tworzenie długiej tabeli przeglądowej i zrobić to bezpośrednio w jednym kroku:
dat$house_type_num <- plyr::mapvalues(dat$HouseType, from = c("Semi", "Single", "Row", "Apartment"), to = c(1, 2, 3, 4))
Wynik:
> 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
źródło
Używanie
merge
różni się od wyszukiwania w programie Excel, ponieważ może zduplikować (pomnożyć) dane, jeśli ograniczenie klucza podstawowego nie jest wymuszone w tabeli odnośników lub zmniejszyć liczbę rekordów, jeśli nie używaszall.x = T
.Aby upewnić się, że nie będziesz mieć z tym kłopotów i bezpiecznie wyszukiwać, sugeruję dwie strategie.
Pierwszą z nich jest sprawdzenie liczby zduplikowanych wierszy w kluczu wyszukiwania:
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) }
Zmusi to Cię do usunięcia duplikatu zestawu danych wyszukiwania przed jego użyciem:
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 # ...
Drugą opcją jest odtworzenie zachowania programu Excel poprzez pobranie pierwszej pasującej wartości z zestawu danych wyszukiwania:
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")
Te funkcje różnią się nieco od
lookup
funkcji dodawania wielu kolumn.źródło