Estoy trabajando con una API que parece devolver datos mal formados. La API debe devolver marcos de datos anidados, pero también devuelve listas vacías en ocasiones:

column_name
<list>
<data.frame [1 × 5]>                
<data.frame [0 × 0]>                
<data.frame [0 × 0]>                
<list [0]>
...

Después de este paso, quiero usar unnest para usar los datos en los marcos de datos anidados aguas abajo. Sin embargo, las listas vacías evitan que esto suceda. Lo que pensé hacer es:

  • (1) Pruebe para ver si la entrada de la fila es una lista vacía
  • (2) En caso afirmativo, convertir a un marco de datos vacío; si no, déjalo como está

Sin embargo, mis enfoques de prueba para las listas vacías han caído un poco, ya que un marco de datos es una lista. Actualmente estoy pensando en usar identical o all.equal junto con dim para la prueba. Es decir, si las dimensiones de la entrada son [1,1], reemplace esta entrada con un marco de datos vacío.

(Me pregunto qué sucede en el caso en que tengo un marco de datos con dimensiones [1,1] pero en realidad también contiene datos ...)

¿Es esta la forma más R de hacer esto? He visto este comportamiento desde la API en otro lugar, por lo que necesitaré usar esta funcionalidad en varios lugares.

Nota: estoy usando el tidyverse, si eso impacta las respuestas.

1
Blue Otter Hat 10 may. 2019 a las 15:58

3 respuestas

La mejor respuesta

A menudo es más fácil limpiar los datos tan pronto como los obtiene de la API. Entonces, todo lo que sigue puede basarse en suposiciones seguras.

Para este ejemplo, cree una función que devuelva un tbl con formato consistente utilizando la respuesta de la API. Cada tbl tendrá las mismas columnas, pero algunas de ellas podrían llenarse con NA si no estuvieran en la respuesta.

library(tidyr)
library(dplyr)

response_to_df <- function(id = NA_real_,
                           country = NA_character_,
                           wealth = NA_real_,
                           ... # Catch extra columns you don't want
                           ) {
  tibble(id = id, country = country, wealth = wealth)
}

prepare_response_df <- function(response) {
  do.call(response_to_df, response)
}

responses <- list(
  tibble(id = 1:2, country = c("US", "DE"), wealth = c(95, 84)),
  list(),
  tibble(id = 3)
)

tibble(res = responses) %>%
  mutate(nicer = lapply(res, prepare_response_df)) %>%
  unnest(nicer)
# # A tibble: 4 x 3
#      id country wealth
#   <dbl> <chr>    <dbl>
# 1     1 US          95
# 2     2 DE          84
# 3    NA NA          NA
# 4     3 NA          NA
0
Nathan Werth 10 may. 2019 a las 13:25

Un marco de datos es un list especial pero la clase es dataframe. Puedes probar la clase de esta manera:

class(data.frame()) == "list"
> FALSE
class(list()) == "list"
> TRUE
1
Clemsang 10 may. 2019 a las 13:07

Aquí hay una opción usando map y if

library(dplyr)
library(purrr)  
ir %>% mutate(data1=map(data, ~if(is.null(dim(.x))) data.frame() else .x)) %>% 
       unnest(data1)

Datos: siempre es útil proporcionar datos reproducibles de copiar y pegar

ir <- iris %>% group_by(Species) %>% nest()
ir$data[[2]]<-list()
1
A. Suliman 10 may. 2019 a las 13:12