Reputation: 1513
Context:
I have a list of data frames, each containing with different dimensions and column data types. My final objective is to create a named vector containing the column name and data type, that I can use to explicitly assign field types for writing a table into a database - the field.types
argument in DBI::dbWriteTable()
Approach:
My current method is to extract the data type of the columns in the list using class
, modifying it so that so I can use it as the named vector to in the field.types
argument. I need to assess whether the data are truly integer values and I wrote a function to do that based on this SO post.
Objective:
I want to use the list output from my own function to identify and modify the data type in the named vector that are truly integer values.
Problem:
I want to replace the elements in the list list_class
based on another list list_int
with logical vectors. I can do this simple replacement/assignment for single data frame but I run into subset issues when using a list of data frames. I've included a reproducible example and some attempts at this below.
library(purrr)
list_df <- list(
df1 = data.frame(v1 = seq(1,10,2),
v2 = seq(1,5,1),
v3 = seq(1,10,length.out = 5)),
df2 = data.frame(v2 = c(seq(1,5), NA),
v3 = seq(2,7,1),
v4 = rep(pi,6)),
df3 = data.frame(v3 = seq(1,2,length.out = 5),
v4 = sample(letters,5),
v5 = seq(1,10,2),
v6 = seq(1,5,1))
)
list_class <- map(list_df, ~map_chr(., class)) #named vector
check_int <- function(v) { #check if truly integer value
if (!is.numeric(v)) FALSE
else all((v%%1 == 0)[!is.na(v%%1 == 0)])
}
list_int <- map(list_df, ~map_lgl(., ~check_int(.)))
For a single data frame below works
list_class[[1]][list_int[[1]]] <- "newdatatype"
And I am able to extract the subset that I want from the list using base Map
.
Map('[', list_class, list_int)
Looking for insight to how put all these pieces together or if my approach is completely off?
Upvotes: 2
Views: 428
Reputation: 66819
You can do...
Map(replace, list_class, list_int, "newdatatatype")
# or
Map(function(x, p) replace(x, p, "newdatatatype"), list_class, list_int)
This creates a new object rather than modifying list_class
, but you're using the tidyverse and so should not be interested in modifying input anyways, I guess. If you really want to, there's...
library(magrittr)
list_class %<>% Map(
f = function(x, p) replace(x, p, "newdatatatype"),
p = list_int
)
Side note: If you look at the code in replace
, you'll see it's just a wrapper / convenience function for what Ryan's code does more directly.
Upvotes: 3
Reputation: 28695
A simple for
loop should work, but you could also use map2
if you wanted a purrr
solution.
for Loop:
for(i in seq_along(list_class))
list_class[[i]][list_int[[i]]] <- "newdatatype"
purrr:
map2(list_class, list_int, ~{.x[.y] <- 'newdatatype'; .x})
output:
# $df1
# v1 v2 v3
# "newdatatype" "newdatatype" "numeric"
#
# $df2
# v2 v3 v4
# "newdatatype" "newdatatype" "numeric"
#
# $df3
# v3 v4 v5 v6
# "numeric" "factor" "newdatatype" "newdatatype"
Upvotes: 3