Tengo una tabla de datos, dt, que parece

   location year value
       NYC 2026     1
       NYC 2026     2
       NYC 2026     3
       NYC 2026     4
       NYC 2026     5
        LA 2026     6
        LA 2026     7
        LA 2026     8
        LA 2026     9
        LA 2026    10

Me gustaría agruparlos por city y year y encontrar el segundo elemento más pequeño en la columna value, por cada grupo, donde se ve el resultado deseado:

   location year value
        NYC  2026     2
         LA  2026     7

dt %>% grou_by(location, year) %>% nth(value, 2)

No funcionaría. Cualquier ayuda es apreciada.

La tabla de datos anterior puede ser creada por:

dt <- structure(list(location = c("NYC", "NYC", "NYC","NYC", "NYC", 
                                   "LA", "LA", "LA", "LA", "LA"), 
                 year = c(2026, 2026, 2026, 2026, 2026,
                          2026, 2026, 2026, 2026, 2026),
                 value = c(1, 2, 3, 4, 5,
                           6, 7, 8, 9, 10)),
                 class = "data.table", 
                 row.names = c(NA, -10L))
3
OverFlow Police 11 may. 2019 a las 00:10

3 respuestas

La mejor respuesta

Una posibilidad dplyr podría ser:

df %>%
 group_by(location) %>%
 arrange(value) %>%
 slice(2)

Aquí se agrupa por columna de "ubicación", ordena los valores según la columna de "valor" y luego mantiene el segundo elemento.

  location  year value
  <chr>    <int> <int>
1 LA        2026     7
2 NYC       2026     2

O si los valores en la columna "valor" podrían estar duplicados, entonces puede hacer:

df %>%
 group_by(location) %>%
 distinct(value, .keep_all = TRUE) %>%
 arrange(value) %>%
 slice(2)

O usando filter() en lugar de slice():

df %>%
 group_by(location) %>%
 arrange(value) %>%
 filter(row_number() == 2)

Lo mismo teniendo en cuenta también posibles duplicados:

df %>%
 group_by(location) %>%
 distinct(value, .keep_all = TRUE) %>%
 arrange(value) %>%
 filter(row_number() == 2)

O usando filter() y dense_rank():

df %>%
 group_by(location) %>%
 filter(dense_rank(value) == 2)

Lo mismo teniendo en cuenta también posibles duplicados:

df %>%
 group_by(location) %>%
 distinct(value, .keep_all = TRUE) %>%
 filter(dense_rank(value) == 2)
6
tmfmnk 10 may. 2019 a las 21:26

Usando summarize para trabajar con group_by:

> dt %>% group_by(location, year) %>% arrange(value) %>%  summarize(value = nth(value, 2))
# A tibble: 2 x 3
# Groups:   location [2]
  location  year value
  <chr>    <dbl> <dbl>
1 LA        2026     7
2 NYC       2026     2
4
liuminzhao 10 may. 2019 a las 21:28

Como solo necesita el segundo elemento, una clasificación parcial no debería estar fuera de discusión. No sé si es compatible con dplyr o data.table, pero es con clasificación de base R (con opciones restringidas), por ejemplo. algo en la línea de

with(dt, lapply(split(dt, interaction(location, year)), 
  function(x) sort.int(x$value, partial=2)))

Dudo que sea más rápido que dplyr o data.table incluso si están clasificando por completo debido a sus optimizaciones, pero tal vez valdría la pena considerar si la eficiencia es una preocupación.

Ah, también podría ordenar primero, luego agrupar y simplemente seleccionar el enésimo valor de cada agrupación guardando las múltiples rutinas de clasificación para cada grupo.

1
Rorschach 10 may. 2019 a las 21:50