Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export errors unnecessarily if passed x is a list of length one. #385

Closed
cha-petersumm opened this issue Nov 2, 2023 · 6 comments
Closed

Comments

@cha-petersumm
Copy link

cha-petersumm commented Nov 2, 2023

It would be neater if the export function worked when passed a dataframe list with length = 1, regardless of the export format.

An error would still occur if length > 1 and the export format didn't support it.

This would avoid the need for this sort of code:

if (length(data_list) > 1 | info$export_function == "writexl::write_xlsx")
export(data_list, output_filename)
else
export(data_list[[1]], output_filename)

@cha-petersumm
Copy link
Author

I haven't tested this, but I think it can be done in export.R by replacing

if (!is.data.frame(x) && !format %in% c("xlsx", "html", "rdata", "rds", "json", "qs", "fods", "ods")) {
stop("'x' is not a data.frame or matrix", call. = FALSE)
}

with

if (!is.data.frame(x) && !format %in% c("xlsx", "html", "rdata", "rds", "json", "qs", "fods", "ods")) {
if (is.list(x) && (length(x) == 1) && is.data.frame(x[[1]]))
x <- x[[1]]
else
stop("'x' is not a data.frame, matrix or list of exactly one data.frame", call. = FALSE)
}

@chainsawriot
Copy link
Collaborator

@cha-petersumm Thank you for reporting and suggesting a solution.

There will be an update. But I will put

if (is.list(x) && (length(x) == 1) && is.data.frame(x[[1]])) {
    x <- x[[1]]
}

in an outer loop.

chainsawriot added a commit that referenced this issue Nov 10, 2023
@cha-petersumm
Copy link
Author

No problem.

You want to avoid doing this for file formats where it's not necessary. In particular, for Excel files, exporting a list preserves the sheet names whereas converting a list of length = 1 to the first element and then exporting will lose them.

@chainsawriot
Copy link
Collaborator

Please install the latest version

install.packages("rio", repos = "https://gesistsa.r-universe.dev")

And there are tests for preserving sheet names when length(x) == 1.

@cha-petersumm
Copy link
Author

Great work! Thanks.

@cha-petersumm
Copy link
Author

Also, in case it's ever useful to you, this is my example of where it was used. It's written to de-identify files containing patient data:

cat(---
title: "deidentify patient data"
author: "Peter Summers"
date: "r Sys.Date()"
output: html_document

knitr::opts_chunk$set(echo = TRUE)

library(tidyverse)
library(rio)
library(tools)
library(openssl)

Deidentify the chosen files.


password <- "Put a project-specific password here."

input_filename_list <- choose.files()

for (input_filename in input_filename_list) {

  output_filename <- paste0(file_path_sans_ext(input_filename)," - deidentified.", file_ext(input_filename))
  
  info <- get_info(input_filename)
  
  data_list <- import_list(input_filename, format=info$format)
  
  # Process multiple data frames if there is more than one, e.g. from a multi-sheet Excel file.
  for (dataset_number in 1:length(data_list)) {
    for (column_name in c("MRN","PAT_ID","CSN","LOG_ID","ORDER_ID")) {
      if (!is.null(data_list[[dataset_number]][[column_name]])) 
        data_list[[dataset_number]][[column_name]] <- md5(as.character(data_list[[dataset_number]][[column_name]]), key=password)
    }
  }
  
# Uncomment the three lines below if not using a version of rio with fix #385.
  # if (length(data_list) > 1 | info$export_function == "writexl::write_xlsx")
    export(data_list, output_filename, format=info$format)
  # else
  #   export(data_list[[1]], output_filename, format=info$format)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants