【问题标题】:How to assign the output of a sapply loop to the original columns in a data frame without losing other columns如何将 sapply 循环的输出分配给数据框中的原始列而不丢失其他列
【发布时间】:2018-04-02 14:05:31
【问题描述】:

我是一个具有不同列的数据框,其中包含来自不同评估者的字符串答案,他们在答案中使用了随机的大写或小写。我想将所有内容都转换为小写。我的代码如下:

# Creating a reproducible data frame similar to what I am working with
dfrm <- data.frame(a = sample(names(islands))[1:20],
               b = sample(unname(islands))[1:20],
               c = sample(names(islands))[1:20],
               d = sample(unname(islands))[1:20],
               e = sample(names(islands))[1:20],
               f = sample(unname(islands))[1:20],
               g = sample(names(islands))[1:20],
               h = sample(unname(islands))[1:20])
# This is how I did it originally by writing everything explicitly:
dfrm1 <- dfrm
dfrm1$a <- tolower(dfrm1$a)
dfrm1$c <- tolower(dfrm1$c)
dfrm1$e <- tolower(dfrm1$e)
dfrm1$g <- tolower(dfrm1$g)
head(dfrm1) #Works as intended

问题是随着评估者数量的增加,我不断地复制粘贴错误。我试图通过为tolower 编写一个函数来简化我的代码,并使用sapply 循环它,但最终的数据帧看起来不像我想要的:

# function and sapply:
dfrm2 <- dfrm
my_list <- c("a", "c", "e", "g")
my_low <- function(x){dfrm2[,x] <- tolower(dfrm2[,x])}
sapply(my_list, my_low) #Didn't work

# Alternative approach:
dfrm2 <- as.data.frame(sapply(my_list, my_low))
head(dfrm2) #Lost the numbers

我错过了什么?

我知道这一定是一个非常基本的概念,我没有得到。有this question and answer that I simply couldn't followthis one where my non-working solution simply seems to work。任何帮助表示赞赏,谢谢!

【问题讨论】:

    标签: r dataframe sapply


    【解决方案1】:

    也许您想创建一个逻辑向量来选择要更改的列并仅在这些列上运行应用函数。

    # only choose non-numeric columns
    changeCols <- !sapply(dfrm, is.numeric)
    
    # change values of selected columns to lower case
    dfrm[changeCols] <- lapply(dfrm[changeCols], tolower)
    

    如果您有其他类型的列,比如逻辑列,您还可以更明确地说明要更改的列类型。例如,要仅选择因子和字符列,请使用。

    changeCols <- sapply(dfrm, function(x) is.factor(x) | is.character(x))
    

    【讨论】:

    • 在这里使用lapplysapply 有什么好处? (避开as.data.frame?)
    • sapplylapply 的包装器,它使用simplyfy2array 作为最终输出,以便在不丢失信息的情况下尽可能地简化输出。这种需要一些计算量的简化在这里是不必要的。我们想要的只是将各个列表项(字符变量)的内容放入其原始槽中。
    • 哦,对了,它不是dfrm[changeCols] &lt;- lapply(dfrm[changeCols], tolower),而是dfrm[changeCols] &lt;- lapply(changeCols, tolower)。您可以编辑以反映吗?谢谢!
    • 我不知道为什么dfrm[changeCols] &lt;- lapply(changeCols, tolower) 会起作用。如果 dfrm 是一个 data.frame,它是一个列表,dfrm[changeCols] 选择感兴趣的列表元素。 lapply(dfrm[changeCols], FUN) 将遍历每个感兴趣的列表项并对其应用 FUN。 lapply(changeCols, FUN) 将遍历逻辑向量,changeCols 并将 FUN 应用于向量中的每个 TRUE 或 FALSE。
    • 这看起来像一个“索引越界”错误。您是否在 dfrm 上创建了 changeCols,然后在 lapply 的 dfrm2 上使用该列?
    【解决方案2】:

    对于您的第一次尝试,如果您希望对数据框 dfrm2 的赋值保持不变,请使用 &lt;&lt;- 赋值运算符:

    my_low <- function(x){ dfrm2[,x] <<- tolower(dfrm2[,x]) }
    sapply(my_list, my_low)
    

    Demo

    【讨论】:

    • 我读到&lt;&lt;- 是一个非常危险的工具,对吗?
    • @naco 是父作用域赋值运算符,但也许你在这里使用它是有意义的。它确实非常类似于双尖矛;也许这就是它名声如此糟糕的原因。
    猜你喜欢
    • 1970-01-01
    • 2020-02-16
    • 2018-09-02
    • 2014-10-03
    • 2022-11-06
    • 1970-01-01
    • 2015-11-21
    • 1970-01-01
    • 2021-02-17
    相关资源
    最近更新 更多