将带有列表列的数据框保存为CSV文件

10

我有一个数据框,它看起来像这样(作为列表的3列)。

A tibble: 14 x 4
                                                    clinic_name drop_in_hours appointment_hours   services
                                                          <chr>        <list>            <list>     <list>
     1                   Birth Control and Sexual Health Centre    <list [1]>        <list [1]> <list [1]>
     2 Black Creek Community Health Centre (Sheridan Mall Site)    <list [1]>        <list [1]> <list [1]>
     3 Black Creek Community Health Centre (Yorkgate mall Site)    <list [1]>        <list [1]> <list [1]>
     4                                         Crossways Clinic    <list [1]>        <list [1]> <list [1]>
     5                                       Hassle Free Clinic    <list [1]>        <list [1]> <list [1]>
     6                          Immigrant Women's Health Center    <list [1]>        <list [1]> <list [1]>
     7                          Rexdale Community Health Center    <list [1]>        <list [1]> <list [1]>
     8                            Rexdale Youth Resource Center    <list [1]>        <list [1]> <list [1]>
     9                         Scarborough Sexual Health Clinic    <list [1]>        <list [1]> <list [1]>
    10                                 Special Treatment Clinic    <list [1]>        <list [1]> <list [1]>
    11                            Taibu Community Health Center    <list [1]>        <list [1]> <list [1]>
    12                                                 The Gate    <list [1]>        <list [1]> <list [1]>
    13                                   The Jane Street Clinic    <list [1]>        <list [1]> <list [1]>
    14                                            The Talk Shop    <list [1]>        <list [1]> <list [1]>

我想将它输出为csv文件。注意,数据框的列在R中不应该是列表。因此,我做了一些谷歌搜索,并找到了这个使用列表列保存数据框,所以我试了一下:

library(tidyverse)

df %>% 
  mutate(drop_in_hours = map_chr(drop_in_hours, ~ capture.output(dput(.))),
         appointment_hours = map_chr(appointment_hours, ~ capture.output(dput(.))),
         services = map_chr(services, ~ capture.output(dput(.)))     ) %>% 
  write_csv("health.csv")

但我遇到了一个错误,这里是否有什么遗漏?
Error in mutate_impl(.data, dots) : 
  Evaluation error: Result 4 is not a length 1 atomic vector

.


我猜你没有像你提到的链接中所示那样将你的 df 转换为 tibble。 - Scipione Sarlo
不,这是一个tibble。 - Ann
你想要在CSV文件中展开列表项吗? - Cybernetic
@Cybernetic 我有一个观察列表 < list(c("星期一:下午2点-下午5点","星期二:下午4点-晚上7点")) ,我想将文件保存为csv,以便每个观察(诊所名称)都有单独的一行。 --- 我希望在我的csv文件中有一个包含“星期一:下午2点-下午5点”,“星期二:下午4点-晚上7点”的单行。 - Ann
请查看我的答案。 - Cybernetic
6个回答

17

创建一个包含列表列的tibble:

library(tibble)

clinic_name <- c('bobo center', 'yoyo plaza', 'lolo market')
drop_in_hours <- list(c("Monday: 2 pm - 5 pm", "Tuesday: 4 pm - 7 pm")) 
appointment_hours <- list(c("Monday: 1 pm - 2 pm", "Tuesday: 2 pm - 3 pm")) 
services <- list(c("skin graft", "chicken heart replacement"))

tibb <- data_frame(clinic_name, drop_in_hours, appointment_hours, services)

print(tibb)

enter image description here

编写一个通用函数,将任何列表列转换为字符类型:

set_lists_to_chars <- function(x) {
    if(class(x) == 'list') {
    y <- paste(unlist(x[1]), sep='', collapse=', ')
    } else {
    y <- x 
    }
    return(y)
}

对包含列表列的tibble应用函数:

new_frame <- data.frame(lapply(tibb, set_lists_to_chars), stringsAsFactors = F)

new_frame

enter image description here

将新格式的数据框保存为CSV文件:

write.csv(new_frame, file='Desktop/clinics.csv')

enter image description here

这是一个csv文件,其中列被扩展为常规字符串。

这里有一个全面的函数,只需传入你的tibble和文件名:

tibble_with_lists_to_csv <- function(tibble_object, file_path_name) {
    set_lists_to_chars <- function(x) { 
        if(class(x) == 'list') { y <- paste(unlist(x[1]), sep='', collapse=', ') } else { y <- x  } 
        return(y) }
    new_frame <- data.frame(lapply(tibble_object, set_lists_to_chars), stringsAsFactors = F)
    write.csv(new_frame, file=file_path_name)
}

用法:

tibble_with_lists_to_csv(tibb, '~/Desktop/tibb.csv')

谢谢,这太棒了!我认为“全面函数”需要tibble_object,但是它有tibb - Fons MA
谢谢。tibb 是一个 tibble 对象。 - Cybernetic
是的,但你在函数内调用了尚未定义的“tibb”。 - Fons MA
明白了...已经修好了。谢谢! - Cybernetic
这个函数似乎不是逐行的,如果bobo中心和yoyo广场的答案不同,它将在整个tibble中使用第一行结果。你有相同的问题吗?有什么办法可以使它逐行运行吗? - Hugo Lehoux
正如@HugoLehoux所指出的那样,该函数不是按行计算的。这里有一个轻微的修改,可以按行工作:set_lists_to_chars <- function(x) { if(class(x) == 'list') { y <- sapply(seq(x), function (y) paste(unlist(x[y]), sep='', collapse=', ')) } else { y <- x } return(y) } - Mak

13

这里有另一个可能会更简单的选择。

根据数据不同,逗号分隔的值可能会变得复杂,所以我使用竖线|来分隔列中的值:

library(tidyverse)

starwars %>% 
  rowwise() %>% 
  mutate_if(is.list, ~paste(unlist(.), collapse = '|')) %>% 
  write.csv('df_starwars.csv', row.names = FALSE)

starwarsdplyr 的示例数据框之一。


您可以使用 mutate_if(~any(str_detect(., fixed('|'))), ~str_split(., fixed('|'))) 反转。 - hypothesis

4

我有一个相似的包含列表列的数据框,希望将其保存为csv格式。我找到了以下方法,还学会了如何将列转回列表。

library(tidyverse)

# create a df with a list column
df <- tibble(x=rep(1:5,each=2), y=LETTERS[1:10]) %>%
  group_by(x) %>%
  summarise(z=list(y))

# this throws an error
write_csv(df, "test.csv")

# convert the list column to a string
df2 <- df %>%
  group_by(x) %>% # where x==unique(x)
  mutate(z=paste(z))

# this works
write_csv(df2, "test.csv")

# read the csv
df3 <- read_csv("test.csv")

# reconstruct original df by parsing the strings
# https://dev59.com/ZXI-5IYBdhLWcg3wpqMK
df4 <- df3 %>%
  group_by(x) %>% 
  mutate(z=list(eval(parse(text=z))))

1

你是否有特定的原因想要将列保存为列表?或者,你可以使用unnest并将其保存为csv。下面是示例:

library(tidyverse)
df_list<-data_frame(abc = letters[1:3], lst = list(1:3, 1:3, 1:3))
df_list %>% unnest() %>% write.csv("list.csv")

此外,当您阅读文件时,可以将其嵌套回来。
df <- read.csv("list.csv")[ ,2:3]
df %>% nest(lst)

我为每个具有多个条目的变量创建了列表列(例如,服务有四个条目,预约时间有三个条目等)。我想将每个诊所的所有服务合并成一个列表,以便每个诊所只有一行,并且有一个包含其提供的所有服务的列表变量,而不是为每个诊所的每项服务都有一行。 - Ann
截至2021年,当使用unnest时,我会收到错误提示:'cols'现在是必需的。 - userJT

1

exploratory::list_to_text()将把list列转换为character列。默认值为sep = ", ",如果要写入.csv文件,建议更改为其他内容。

devtools::install_github("exploratory-io/exploratory_func")

list_to_text <- function(column, sep = ", "){
  loadNamespace("stringr")
  ret <- sapply(column, function(x) {
    ret <- stringr::str_c(x, collapse = sep)
    if(identical(ret, character(0))){
      # if it's character(0)
      NA
    } else {
      ret
    }
  })
  as.character(ret)
}

https://github.com/exploratory-io/exploratory_func/blob/master/LICENSE.md


0

继续 @cybernetic 的示例,以下使用 dplyr::mutate_if 的解决方案适用于我。

library(tibble)
library(dplyr)

clinic_name <- c('bobo center', 'yoyo plaza', 'lolo market')
drop_in_hours <- list(c("Monday: 2 pm - 5 pm", "Tuesday: 4 pm - 7 pm")) 
appointment_hours <- list(c("Monday: 1 pm - 2 pm", "Tuesday: 2 pm - 3 pm")) 
services <- list(c("skin graft", "chicken heart replacement"))

tibb <- data_frame(clinic_name, drop_in_hours, appointment_hours, services)

# unlist if column is list
tibb_unlisted <- tibb %>%
  rowwise() %>%
  dplyr::mutate_if(is.list, 
    funs(paste(unlist(.), sep='', collapse=', ')))
# print on screen, can see values
print(tibb_unlisted)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接