Si entiendo su pregunta correctamente, aquí hay cuatro métodos para hacer el equivalente de Excel VLOOKUPy completar usando 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
A continuación, se muestran cuatro métodos para completar HouseTypeNoel largetableuso de los valores de la lookuptabla:
Primero con mergeen base:
base1 <- (merge(lookup, largetable, by = 'HouseType'))
Un segundo método con vectores con nombre en base:
housenames <- as.numeric(1:length(unique(hous$HouseType)))
names(housenames) <- unique(hous$HouseType)
base2 <- data.frame(HouseType = largetable$HouseType,
HouseTypeNo = (housenames[largetable$HouseType]))
En tercer lugar, usando el plyrpaquete:
library(plyr)
plyr1 <- join(largetable, lookup, by = "HouseType")
Cuarto, usando el sqldfpaquete
library(sqldf)
sqldf1 <- sqldf("SELECT largetable.HouseType, lookup.HouseTypeNo
FROM largetable
INNER JOIN lookup
ON largetable.HouseType = lookup.HouseType")
Si es posible que algunos tipos de casas largetableno existan en, lookupse usaría una combinación izquierda:
sqldf("select * from largetable left join lookup using (HouseType)")
También se necesitarían cambios correspondientes en las otras soluciones.
¿Es eso lo que querías hacer? Déjame saber qué método te gusta y agregaré comentarios.
Creo que también puedes usar
match():Esto todavía funciona si mezclo el orden de
lookup.fuente
También me gusta usar
qdapTools::lookupun operador binario o abreviado%l%. Funciona de manera idéntica a un vlookup de Excel, pero acepta argumentos de nombre opuestos a números de columna## 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% housfuente
La solución # 2 de la respuesta de @ Ben no es reproducible en otros ejemplos más genéricos. Da la búsqueda correcta en el ejemplo porque los únicos
HouseTypeenhousesaparecen en orden creciente. Prueba esto: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 solución # 2 da
housenames <- as.numeric(1:length(unique(hous$HouseType))) names(housenames) <- unique(hous$HouseType) base2 <- data.frame(HouseType = largetable$HouseType, HouseTypeNo = (housenames[largetable$HouseType]))que cuando
unique(base2$HouseTypeNo[ base2$HouseType=="ECIIsHome" ]) [1] 2cuando la respuesta correcta es 17 de la tabla de búsqueda
La forma correcta de hacerlo es
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]))Ahora las búsquedas se realizan correctamente
unique(base2$HouseTypeNo[ base2$HouseType=="ECIIsHome" ]) ECIIsHome 17Intenté editar la respuesta de Bens pero fue rechazada por razones que no puedo entender.
fuente
Empezando con:
houses <- read.table(text="Semi 1 Single 2 Row 3 Single 2 Apartment 4 Apartment 4 Row 3",col.names=c("HouseType","HouseTypeNo"))... puedes usar
... para dar un número único para cada tipo de casa. Puedes ver el resultado aquí:
> 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... entonces terminas con diferentes números en las filas (porque los factores están ordenados alfabéticamente) pero con el mismo patrón.
(EDITAR: el texto restante en esta respuesta es en realidad redundante. Se me ocurrió verificar y resultó que
read.table()ya había convertido las casas $ HouseType en un factor cuando se leyó en el marco de datos en primer lugar).Sin embargo, es posible que sea mejor convertir HouseType en un factor, lo que le brindaría los mismos beneficios que HouseTypeNo, pero sería más fácil de interpretar porque los tipos de casas tienen nombres en lugar de numerados, por ejemplo:
> 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"fuente
El póster no preguntó acerca de buscar valores si
exact=FALSE, pero agrego esto como una respuesta para mi propia referencia y posiblemente para otras.Si está buscando valores categóricos, use las otras respuestas.
Excel
vlookuptambién le permite hacer coincidir aproximadamente valores numéricos con el cuarto argumento (1)match=TRUE. Pienso enmatch=TRUEcomo buscar valores en un termómetro. El valor predeterminado es FALSO, que es perfecto para valores categóricos.Si desea hacer coincidir aproximadamente (realizar una búsqueda), R tiene una función llamada
findInterval, que (como su nombre lo indica) encontrará el intervalo / bin que contiene su valor numérico continuo.Sin embargo, digamos que lo desea
findIntervalpara varios valores. Puede escribir un bucle o utilizar una función de aplicación. Sin embargo, me ha resultado más eficiente adoptar un enfoque vectorizado de bricolaje.Digamos que tiene una cuadrícula de valores indexados por xey:
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)))y tiene algunos valores que desea buscar por x e 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")Aquí está el ejemplo visualizado:
contour(grid) points(df$x, df$y, pch=df$id, col="blue", cex=1.2)Puede encontrar los intervalos xy los intervalos y con este tipo de fórmula:
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)) + 1Podría dar un paso más y realizar una interpolación (simplista) en los valores z de
gridesta manera: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)Lo que te da estos valores:
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.00425Tenga en cuenta que ix e iy también podrían haberse encontrado con un bucle usando
findInterval, por ejemplo, aquí hay un ejemplo para la segunda filafindInterval(df$x[2], grid$x) # 4 findInterval(df$y[2], grid$y) # 2Que coincide
ixyiyendf[2]Nota al pie: (1) El cuarto argumento de vlookup se llamaba anteriormente "coincidencia", pero después de que introdujeron la cinta se le cambió el nombre a "[range_lookup]".
fuente
Puede utilizar
mapvalues()el paquete plyr.Datos iniciales:
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 RowTabla de búsqueda / paso de peatones:
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 4Crea la nueva variable:
O para reemplazos simples, puede omitir la creación de una tabla de búsqueda larga y hacer esto directamente en un solo paso:
dat$house_type_num <- plyr::mapvalues(dat$HouseType, from = c("Semi", "Single", "Row", "Apartment"), to = c(1, 2, 3, 4))Resultado:
> 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 3fuente
El uso
mergees diferente de la búsqueda en Excel, ya que tiene el potencial de duplicar (multiplicar) sus datos si la restricción de clave principal no se aplica en la tabla de búsqueda o reducir la cantidad de registros si no los está usandoall.x = T.Para asegurarse de no meterse en problemas con eso y realizar la búsqueda de forma segura, sugiero dos estrategias.
El primero es hacer una verificación en una serie de filas duplicadas en la clave de búsqueda:
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) }Esto lo obligará a eliminar el conjunto de datos de búsqueda antes de usarlo:
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 # ...La segunda opción es reproducir el comportamiento de Excel tomando el primer valor coincidente del conjunto de datos de búsqueda:
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")Estas funciones son ligeramente diferentes de las
lookupque agregan varias columnas.fuente