2010-07-12 16 views
6

Hola a todos, soy nuevo a R.cómo evitar bucles

Tengo dos archivos de datos de panel, con columnas "id", "fecha" y "ret"

archivo de A tiene una muchos más datos que el archivo B, pero estoy trabajando principalmente con datos de archivo B.

La combinación de "id" y "date" es un identificador único.

¿Hay alguna manera elegent de buscar cada uno (id, fecha) en B, necesito obtener los últimos 10 días ret del archivo A, y almacenarlos de nuevo en B?

mi manera ingenua de hacerlo es a lazo para todas las filas de B,

for i in 1:length(B) { 
    B$past10d[i] <- prod(1+A$ret[which(A$id == B$id[i] & A$date > B$date[i]-10 & A$date < B$date[i])])-1 
} 

pero los bucles de toma para siempre.

Realmente aprecio sus pensamientos.

Muchas gracias.

+1

K: Para futuras consultas: Es muy útil para las personas que tratan de ayudar a que si el código coincide con el texto de la pregunta usted pregunta. –

+1

sry my bad. gracias por su ayuda –

+0

Está abierto a interpretación si coincide o no. El código es solo una aclaración ... como de costumbre. – John

Respuesta

0

¿Es esto más rápido? (Estoy asumiendo que la combinación de B $ id y la fecha B $ es un identificador único no replicado en cualquier lugar - implícito en su código)

B$idDate <- factor(B$id):factor(B$date) 
B$past10 <- sapply(B$idDate, function(x){with(B[B$idDate == x,], 
    prod(1+A$ret[A$id == id & A$date > date-10 & A$date < date])-1)}) 
+0

gracias por su respuesta john. Se ve muy limpio. Pero cuando probé con los datos, obtuve: Error: no puedo asignar el vector de tamaño 6.8 Mb ¿demasiado trabajo para el chico de B $ 10? –

+0

Esto no debería requerir mucha más memoria que su método. Intente borrar algunas cosas en la memoria con los comandos ls() y rm(). O intente reiniciar R y ejecutar de nuevo. – John

+0

tks! Lo intentaré ... pero olvidé mencionar que mi método ingenuo nunca terminó de buclear ..: S –

1

¿Usted intentó fusionar??

"Combinar dos tramas de datos por columnas comunes o nombres de las filas, o hacer otras versiones de base de datos de operaciones de combinación."

Además Es mejor utilizar una base de datos pequeña local de MySQL/PostgreSQL (RMySQL/RPostgreSQL) si continuamente compuestos deportivos PK o cualquier otro como identificadores únicos. Para mí, la reorganización de datos SQL y luego el uso de data.frames desde la vista es mucho más fácil que el bucle.

+0

hm .. podría necesitar buscar en sql. gracias! –

+0

RMySQL fue más fácil para comenzar ... –

0

Si no tiene datos que se repliquen en A y B, entonces rbind es la solución más simple.

#Sample data 
A <- data.frame(
    id = rep(letters[1:3], each = 13), 
    date = Sys.Date() + -12:0, 
    ret = runif(39) 
) 

B <- data.frame(
    id = rep(letters[5:6], each = 5), 
    date = Sys.Date() + -4:0, 
    ret = runif(10) 
) 

#Only take the last ten days from A 
A_past_10_days <- A[A$date > Sys.Date() - 10,] 

#Bind by rows 
rbind(A_past_10_days, B) 
+0

Usted está tomando los últimos 10 días a partir de hoy, pero su código toma los últimos 10 días de cada fecha posible en B calificados por el identificador que va con esa fecha. – John

+0

@John: Bien visto. Parece que he respondido el texto de la pregunta, pero no la pregunta oculta en el código. ** Suspiro ** –

0

En general, debe evitar el bucle en R. Es mucho más rápido si su código opera en vectores.

Utilizaría la fusión, como lo sugiere ran2. Puede configurar all.x = T (o all.y o all) para obtener todas las filas de uno (u otro o ambos) marcos de datos. Esto es rápido y, por lo general, resolverá qué campos coincidir por sí mismo. De lo contrario, deberá especificar by.x (y by.y o by) como campo de búsqueda. Por el sonido de la misma, puede que necesite crear este campo usted mismo (según el comentario de John).

Puede filtrar por fecha.

+0

tks! B es en realidad un subconjunto mucho más pequeño de A, así que si entiendo 'fusionar' correctamente, entonces A sería el resultado de la fusión. –

+0

Esto ocupará mucha más memoria que su solución original. Dado que estaba llegando a los límites de memoria con mi sugerencia, es poco probable que funcione. Además, todo lo que hace es obtener los datos en un solo lugar. No resuelve su problema de convertir esos últimos 10 días en un solo valor ... que su código sugiere que es necesario. – John

+0

¡Ah! Mi error. Pensé que estarías obteniendo un conjunto de filas de A a B. No me había dado cuenta de que realmente querías resumir el producto.También puede estar interesado en agregado (la función de conveniencia para sapply). Si tiene problemas de memoria, entonces puede que desee subconjunto tomar una pequeña muestra que puede practicar hasta que esté seguro de que el código funciona (ver también: http://www.r-bloggers.com/memory-management -in-ra-few-tips-and-tricks /). – RobinGower

1

Creo que la clave es vectorizar y usar el operador %in% para subconjunto del marco de datos A. Y, lo sé, los precios no son números aleatorios, pero no quería codificar una caminata aleatoria ... Creé un índice de fecha de stock usando paste, pero estoy seguro de que podría usar el índice de pdata.frame en el plm biblioteca, que es la mejor que he encontrado para los datos del panel.

A <- data.frame(stock=rep(1:10, each=100), date=rep(Sys.Date()-99:0, 10), price=rnorm(1000)) 
B <- A[seq(from=100, to=1000, by=100), ] 
A <- cbind(paste(A$stock, A$date, sep="-"), A) 
B <- cbind(paste(B$stock, B$date, sep="-"), B) 
colnames(A) <- colnames(B) <- c("index", "stock", "date", "price") 
index <- which(A[, 1] %in% B[, 1]) 
returns <- (A$price[index] - A$price[index-10])/A$price[index-10] 
B <- cbind(B, returns) 
0

Dado que usted está teniendo problemas de memoria, tal vez disminuyendo Un primero podría ayudar. Primero, deshazte de los ids extraños.

A <- A[A$id %in% B$id,] 

Reducir el conjunto de datos completamente todavía quiere tomar más memoria. No es posible sin almacenar algunas variables. Sin embargo, podemos deshacernos de muchos de ellos, espero reducir cada fecha por debajo de nuestro mínimo absoluto y por encima de nuestro máximo absoluto.

A <- A[A$date > (min(B$date) - 10) & A$date <= max(B$date),] 

Por supuesto, al no calificar esta por id tenemos no conseguir la versión más pequeña de una posible, pero es de esperar que es lo suficientemente pequeño.

Ahora ejecuta el código que propuso por primera vez y ver si todavía tiene un error de memoria

B$idDate <- factor(B$id):factor(B$date) 
B$past10 <- sapply(B$idDate, function(x){with(B[B$idDate == x,], 
    prod(1+A$ret[A$id == id & A$date > date-10 & A$date < date])-1)}) 
+0

Gracias John, Las dos líneas ayudan a reducir mucho en A. pero después de intentar con solo 500 filas, todavía me dio el error: no se puede asignar un vector de tamaño de 6,8 Mb. creo que el problema podría estar en 'sapply'. de alguna manera está tomando las cosas de identificación y fecha como vector y tratando de comparar A $ id contra B $ id, etc., en lugar de por el elemento –

+0

parece que es sapply (B $ idDate) que está creando el problema, cambié B $ idDate a chars y el código ha estado funcionando ........ huele como un bucle –

+0

intente no poner pasado 10 en B (eliminar B $ al comienzo de la segunda línea). ¿Eso también tiene un problema de memoria? sapply() toma cada elemento de B y luego lo usa para subselecular A y obtener el producto ... como en el código original. Lo demostré en mi código pero en mi computadora 6.8meg es una cantidad trivial de memoria (y solo estoy usando unas 200 líneas). Y si informa un mensaje de error, ¿cuál es el mensaje exacto? – John

0
library(data.table) 
#create data 
A <- data.table(id=rep(1:10, each=40000), date=rep(Sys.Date()-99:0, 4000), ret=rnorm(400000)) 
B <- data.table(id=rep(1:5, each=10), date=rep(Sys.Date()-99:0), ret=rnorm(50)) 

#find dates to compare against 
n <- NROW(B) 
B_long <- B[,.(id = rep(id,each=10),date = rep(date,each=10))] 
s <- rep(-10:-1,n) 
B_long[,date:=date + s] 

#information in one column 
B_long$com <- as.numeric(paste0(B_long$id,as.numeric(B$date))) 
A$com <- as.numeric(paste0(A$id,as.numeric(A$date))) 

#compare 
setkey(A,com) 
X <- A[com %in% B_long$com,] 

Esta respuesta se basa en respuesta Richards, pero es más específico a la pregunta.

La idea clave es crear un vector de combinaciones de fechas de id para comparar. Esto sucede en el segundo bloque de código.

Mi solución utiliza el paquete data.table pero con algunos cambios de sintaxis debería funcionar con un data.frame. Pero el uso del paquete data.table tiene la ventaja de keycolumns.

Si todavía tiene problemas para realizar la vinculación de este enfoque con la segunda respuesta de Juan y primera cosecha A.