嗨,我在R中有这个数据框:
m2 <- c(22,NA,0,NA,42,NA)
m3 <- c(89,38,0,67,0,NA)
df = data.frame(m2,m3)
我想计算m3和m2之间的收益。公式为:return =(m2 [i]-m3 [i])/ m3 [i]。要计算的条件是:
到目前为止,我已经尝试了以下代码:
for (i in nrow(df)){
if (is.na(df[['m2']][i]) == TRUE | is.na(df[['m3']][i]) == TRUE){df[['result']][i] = NA}
if (df[['m2']][i] == 0 & df[['m3']][i] == 0) {df[['result']][i] = 9999}
if (df[['m3']][i] == 0 | df[['m2']][i] != 0) {df[['result']][i] = -9999}
else {df[['result']][i] = (df[['m2']][i] - df[['m3']][i])/df[['m3']][i]}
}
但是它返回如下:
Error in if (df[["m2"]][i] == 0 & df[["m3"]][i] == 0) { :
missing value where TRUE/FALSE need
我已经尝试了相同的python方法,并且可以正常工作。有什么方法可以在R中做到这一点,是否应该在不使用for循环的情况下计算收益?
答案 0 :(得分:3)
如果您想提高可读性,可以选择case_when
中的dplyr
:
library(dplyr)
df %>%
mutate(
result = case_when(
is.na(m2) | is.na(m3) ~ NA_real_,
m2 == 0 & m3 == 0 ~ 9999,
m2 != 0 & m3 == 0 ~ -9999,
TRUE ~ (m2 - m3) / m3
)
)
正如@markus所添加的,您确实可以跳过第一行以获得相同的输出。
我还建议阅读?case_when
帮助页面以了解一些具体信息(例如订单的相关性,跳过TRUE
,为什么在上述情况下使用NA_real_
,等)。
答案 1 :(得分:2)
我将打破这两个步骤:
m2 <- c(22,NA,0,NA,42,NA)
m3 <- c(89,38,0,67,0,NA)
df = data.frame(m2,m3)
df$return <- with(df, (m2 - m3)/m3)
df$return <- with(df, ifelse(m2 == 0 & m3 == 0, -9999, ifelse(m2 != 0 & m3 == 0, 9999, return)))
由reprex package(v0.2.1)于2019-01-24创建
这里要注意的事情包括:1)使用ifelse()
,因为它是矢量化的(即将自然地对df的所有行进行操作,而不必编写for循环代码; 2)R自然会产生{{1 }}如果NA
或m2
是m3
,那么您只需说明NA
等于return
或9999
的条件。
答案 2 :(得分:1)
m2 <- c(22,NA,0,NA,42,NA)
m3 <- c(89,38,0,67,0,NA)
df = data.frame(m2,m3)
library(tidyverse)
df %>% mutate( return = ifelse(is.na(df$m2)|is.na(df$m3), NA, ifelse(df$m2 == 0 & df$m3 == 0, 9999, ifelse(df$m3 == 0 & df$m2 != 0, -9999, (df$m2 - df$m3)/df$m3))) )
答案 3 :(得分:1)
您的逻辑真正要说明的是您要更改:
NaN to -9999 (occurs for 0/0)
Inf to 9999 (occurs for x/0)
因此您可以简单地应用公式,然后替换。对我来说,看起来似乎有些困惑。我会尽可能避免使用“ if-then”逻辑。
基本R解决方案:
df$return <- (df$m2 - df$m3) / df$m3
df[is.nan(df$return),"return"] <- -9999
df[is.infinite(df$return),"return"] <- 9999
dplyr解决方案:
library(dplyr)
df %>%
mutate(return = (m2 - m3) / m3,
return = if_else(is.nan(return), -9999, return),
return = if_else(is.infinite(return), 9999, return))