【问题标题】:Prevent change to dataframe format in R防止更改 R 中的数据帧格式
【发布时间】:2023-02-04 02:14:58
【问题描述】:

我有一个必须具有特定布局的数据框。有没有办法让 R 拒绝我尝试更改列数或名称的任何命令?

手动检查数据表的格式很容易,但我没有找到让 R 在每次执行一段代码时自动为我做的方法。

问候

【问题讨论】:

  • 这是一个有点奇怪的要求。为什么要运行可能会在未经您许可的情况下更改列的名称/编号的代码?最明智的做法是制作数据框的副本。例如,如果您的数据框名为df,则只需执行df_backup <- df。如果您以某种方式搞砸了数据框,请使用df <- df_backup 恢复原始的df。我可以想到其他方法,但这确实是最简单的。
  • 想到的两个半生不熟的想法是:(1)将您的数据保存在具有锁定列的远程数据库中,并使用 DBI、dbplyr 等进行操作;或 (2) 使用只允许对数据进行允许更改的方法的 R6 对象。注意,我缺乏数据库管理或 R6 的知识,不知道这两者是否真的可行。
  • 使用 data.table,也许可以试试:setattr(DT, '.data.table.locked', TRUE),见stackoverflow.com/q/44995065/680068
  • 对于@AllanCameron 关于创建df_backup 的评论,请注意,如果您的对象属于data.table 类,那么您需要显式copy(df)。作为原因的示例,请尝试:A <- data.table(a=1:2); B <- A; A[,b:=3:4],并注意B 有列b;这将 Allan 的评论修改为 df_backup <- copy(df)
  • 请提供足够的代码,以便其他人可以更好地理解或重现问题。

标签: r data.table format


【解决方案1】:

这不提供我认为您正在寻找的万无一失的安全级别(如果没有更多详细信息,很难知道),但您可以定义一个 function operator,如果检测到对列的更改,它会产生错误的修改函数:

same_cols <- function(fn) {
  function(.data, ...) {
    out <- fn(.data, ...)
    stopifnot(identical(sort(names(.data)), sort(names(out))))
    out
  }
}

例如,您可以创建 dplyr 函数的修改版本:

library(dplyr)

my_mutate <- same_cols(mutate)
my_summarize <- same_cols(summarize)

如果保留列,它将照常工作:

mtcars %>%
  my_mutate(mpg = mpg / 2) %>%
  head()
#                     mpg cyl disp  hp drat    wt  qsec vs am gear carb
# Mazda RX4         10.50   6  160 110 3.90 2.620 16.46  0  1    4    4
# Mazda RX4 Wag     10.50   6  160 110 3.90 2.875 17.02  0  1    4    4
# Datsun 710        11.40   4  108  93 3.85 2.320 18.61  1  1    4    1
# Hornet 4 Drive    10.70   6  258 110 3.08 3.215 19.44  1  0    3    1
# Hornet Sportabout  9.35   8  360 175 3.15 3.440 17.02  0  0    3    2
# Valiant            9.05   6  225 105 2.76 3.460 20.22  1  0    3    1

mtcars %>% 
  my_summarize(across(everything(), mean))
#        mpg    cyl     disp       hp     drat      wt     qsec     vs      am
# 1 20.09062 6.1875 230.7219 146.6875 3.596563 3.21725 17.84875 0.4375 0.40625
#     gear   carb
# 1 3.6875 2.8125

但如果对列进行了更改,则会抛出错误:

mtcars %>%
  my_mutate(mpg2 = mpg / 2)
# Error in my_mutate(., mpg2 = mpg/2) : 
#   identical(sort(names(.data)), sort(names(out))) is not TRUE

mtcars %>%
  my_summarize(mpg = mean(mpg))
# Error in my_summarize(., mpg = mean(mpg)) : 
#   identical(sort(names(.data)), sort(names(out))) is not TRUE

【讨论】:

    【解决方案2】:

    您提到名称和列需要相同,还意识到 data.table 名称也通过引用更新。请参见下面的示例。

    foo <- data.table(
      x = letters[1:5],
      y = LETTERS[1:5]
    )
    
    colnames <- names(foo)
    
    colnames
    # [1] "x" "y"
    
    setnames(foo, colnames, c("a", "b"))
    foo[, z := "oops"]
    
    colnames
    # [1] "a" "b" "z"
    
    identical(colnames, names(foo))
    # [1] TRUE
    

    检查那个两个都列和名称未更改(此处顺序相同),您可以立即获取名称的副本。在每次代码运行后,您可以使用复制的名称检查当前名称。

    foo <- data.table(
      x = letters[1:5],
      y = LETTERS[1:5]
    )
    
    colnames <- copy(names(foo))
    
    setnames(foo, colnames, c("a", "b"))
    foo[, z := "oops"]
    
    identical(colnames, names(foo))
    [1] FALSE
    
    colnames
    # [1] "x" "y"
    
    names(foo)
    # [1] "a" "b" "z"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多