Problemas resueltos en R (I)
8 Jun 2018 9 minsSeleccionar filas segun resultados de una agregación
Suponiendo que tengamos un data.frame o similar como el siguiente:
df <- data.frame(year=c(2010, 2010, 2011, 2012, 2012, 2010, 2010, 
						2011, 2010, 2011, 2011, 2012), 
                 colegio=c(rep("A",5),rep("B",3),rep("C",4)),
                 alumno=c(1,2,1,1,2,1,2,1,1,2,2,1))
df
   year colegio alumno
1  2010       A      1
2  2010       A      2
3  2011       A      1
4  2012       A      1
5  2012       A      2
6  2010       B      1
7  2010       B      2
8  2011       B      1
9  2010       C      1
10 2011       C      2
11 2011       C      2
12 2012       C      1
Y lo que buscamos es filtrar aquellas filas cuyos colegios tengan observaciones en los tres años. Para esto lo primero es hacer una agregación por colegio y contar la cantidad de años distintos que tiene cada uno:
colegio <- aggregate(year ~ colegio, df, function(x) {length(unique(x))})
colegio
  colegio year
1       A    3
2       B    2
3       C    3
El uso de aggregate() no ofrece mucha dificultad, lo unico particular es la función de agregación que hemos definido, nos quedamos con los valores únicos de cada año mediante unique() y luego sencillamente conatmos cuanto son mediante length()
Ahora simplemente nos quedamos con las filas cuyos colegios tengan una cantidad en year igual al valor maximo de esta columna:
df[df$colegio %in% as.character(colegio[colegio$year == max(colegio$year),1]),]
  year colegio alumno
1  2010       A      1
2  2010       A      2
3  2011       A      1
4  2012       A      1
5  2012       A      2
9  2010       C      1
10 2011       C      2
11 2011       C      2
12 2012       C      1
En caso de usar dplyr, la mecanica es similar, aunque notablemente más clara:
library(dplyr)
df %>% 
    inner_join(df %>%
                   group_by(colegio) %>%
                   summarize(cant = n_distinct(year)) %>%
                   filter(cant == max(cant))
    )
fuente: Eliminar observaciones que no aparecen en múltiples años en R
Resolver un sistema de ecuaciones con matrices no cuadradas
Supongamos que tenemos un sistema de ecuaciones lineales como el siguiente:
-a + b/2             = 0
 a - b   + c/2       = 0
     b/2 + -c  + d   = 0
         + c/2 - d   = 0
 a + b   +  c  + d   = 1
Si lo llevamos a una matriz:
a1 <- rbind(c(-1,0.5,0,0),c(1,-1,0.5,0),c(0,0.5,-1,1),
				c(0,0,0.5,-1),c(1,1,1,1))
a1
     [,1] [,2] [,3] [,4]
[1,]   -1  0.5  0.0    0
[2,]    1 -1.0  0.5    0
[3,]    0  0.5 -1.0    1
[4,]    0  0.0  0.5   -1
[5,]    1  1.0  1.0    1
Y el vector de igualación sería:
b1 <- c(0,0,0,0,1)
b1
[1] 0 0 0 0 1
Para resolver este sistema, no nos sirve solve() ya que su funcionamiento esta restringido a matrices cuadradas. Pero si podríamos usar qr.solve() de la siguiente manera:
    qr.solve(a1,b1)
    [1] 0.1666667 0.3333333 0.3333333 0.1666667
O si preferimos ver el resultado como fracciones, puedes usar as.fractions() del paquete MASS:
library(MASS) 
as.fractions(qr.solve(a1,b1))
[1] 1/6 1/3 1/3 1/6
fuente: ¿Cómo resolver un sistema de ecuaciones lineales en R con matriz NO cuadrada?
Usando reshape() y no morir en el intento
Es cierto, es un poco confuso para usar, en general cada vez que lo necesito, tengo que volver a leer la ayuda y los ejemplos ( ?reshape) para acordarme. Primero preparemos un ejemplo:
df <- read.table(text="Code Espece avreviada 2005 2006 2007 2008 2009
ST stypopodium trascend   15  22.5 19.5  2.5 23.14
AG  anfiroa fragilisim    2.2 12.5 15.7 11.3 12.2", header=T, strip.white = T, stringsAsFactors=F)
colnames(df) <- c('Code','Espece','avreviada','2005','2006','2007','2008','2009')
df
	Code      Espece  avreviada 2005 2006 2007 2008  2009
1   ST stypopodium   trascend 15.0 22.5 19.5  2.5 23.14
2   AG     anfiroa fragilisim  2.2 12.5 15.7 11.3 12.20
Claramente está en un formato “ancho” y lo que buscaríamos es pasarlo al formato “largo”, lo que hay que expandir son los valores de los años. La forma más básica de hacerlo es la siguiente:
reshape(data=df, 
		direction="long", 
		varying=list(4:8), 
		times=names(df)[4:8], 
		timevar="Year", 
		v.names="Valor")
Tres parámetros mínimos son suficientes en este caso:
- 
data: Es eldata.framea ajustar
- 
direction:longowide
- 
varying: Independientemente de lo que diga la ayuda, es una lista con los nombres o lo índices de las columnas que transformaremos de su distribución horizontal a vertical, en nuestro ejemplo son las columnas2005,2006,2007,2008,2009, se pueden indicar por el número:list(4:8)o por nombrelist(names(df)[4:8]).
Parámetros opcionales:
- 
times: Para indicar que valores vamos a trasladar, si no indicamos nada será un índice, haciendotimes=names(df)[4:8]trasladamos directamente el nombre de la columna.
- 
timevar, para indicar el nombre de la columna dónde se trasladarán el nombre de cada año.
- 
v.names, para indicar el nombre de la columna dónde se trasladarán los valores
La salida:
       Code      Espece  avreviada Year Valor id
1.2005   ST stypopodium   trascend 2005 15.00  1
2.2005   AG     anfiroa fragilisim 2005  2.20  2
1.2006   ST stypopodium   trascend 2006 22.50  1
2.2006   AG     anfiroa fragilisim 2006 12.50  2
1.2007   ST stypopodium   trascend 2007 19.50  1
2.2007   AG     anfiroa fragilisim 2007 15.70  2
1.2008   ST stypopodium   trascend 2008  2.50  1
2.2008   AG     anfiroa fragilisim 2008 11.30  2
1.2009   ST stypopodium   trascend 2009 23.14  1
2.2009   AG     anfiroa fragilisim 2009 12.20  2
Los rowname y la columna id son datos colaterales que genera la función.
fuente: ¿Cómo convertir un data.frame en un formato horizontal a uno vertical?
