Zac*_*ach 136 datetime if-statement r
我正在使用该函数ifelse()
来操作日期向量.我期望结果是类Date
,并且很惊讶地得到了一个numeric
向量.这是一个例子:
dates <- as.Date(c('2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04', '2011-01-05'))
dates <- ifelse(dates == '2011-01-01', dates - 1, dates)
str(dates)
Run Code Online (Sandbox Code Playgroud)
这尤其令人惊讶,因为在整个向量上执行操作会返回一个Date
对象.
dates <- as.Date(c('2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04','2011-01-05'))
dates <- dates - 1
str(dates)
Run Code Online (Sandbox Code Playgroud)
我应该使用其他功能来操作Date
矢量吗?如果是这样,有什么功能?如果没有,我如何强制ifelse
返回与输入相同类型的向量?
帮助页面ifelse
表明这是一个功能,而不是一个错误,但我仍然在努力寻找一个解释我发现的令人惊讶的行为.
Hen*_*rik 103
你可以用data.table::fifelse
.
从data.table >= 1.12.3
发行说明:"[ dplyr::if_else
]有更严格的语义data.table::fifelse
:ifelse
和fifelse
参数必须是相同的类型.这给出了一个不太令人惊讶的返回类型,并保留了像日期这样的S3向量".
library(data.table)
dates <- fifelse(dates == '2011-01-01', dates - 1, dates)
str(dates)
# Date[1:5], format: "2010-12-31" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"
Run Code Online (Sandbox Code Playgroud)
42-*_*42- 62
它涉及到记录值的ifelse
:
具有相同长度和属性(包括尺寸和"
class
")的矢量test
和来自yes
或的值的数据值no
.答案的模式将从逻辑强制进行,以适应从中获取的任何值yes
,然后从中获取任何值no
.
归结为它的影响,ifelse
使得因素失去了他们的水平,日期失去了他们的阶级,只有他们的模式("数字")恢复.试试这个:
dates[dates == '2011-01-01'] <- dates[dates == '2011-01-01'] - 1
str(dates)
# Date[1:5], format: "2010-12-31" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"
Run Code Online (Sandbox Code Playgroud)
你可以创建一个safe.ifelse
:
safe.ifelse <- function(cond, yes, no){ class.y <- class(yes)
X <- ifelse(cond, yes, no)
class(X) <- class.y; return(X)}
safe.ifelse(dates == '2011-01-01', dates - 1, dates)
# [1] "2010-12-31" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"
Run Code Online (Sandbox Code Playgroud)
后来的注释:我看到Hadley if_else
在数据整形包的magrittr/dplyr/tidyr复合体中构建了一个.
JD *_*ong 15
DWin的解释是现场.在我意识到我可以在ifelse声明之后简单地强制完课之后,我摆弄并与之斗争了一段时间:
dates <- as.Date(c('2011-01-01','2011-01-02','2011-01-03','2011-01-04','2011-01-05'))
dates <- ifelse(dates=='2011-01-01',dates-1,dates)
str(dates)
class(dates)<- "Date"
str(dates)
Run Code Online (Sandbox Code Playgroud)
起初,这对我来说感觉有点"愚蠢".但是现在我只想把它作为从ifelse()获得的性能回报的一个小代价.此外,它仍然比循环更简洁.
建议的方法不适用于因子列.我想建议这种改进:
safe.ifelse <- function(cond, yes, no) {
class.y <- class(yes)
if (class.y == "factor") {
levels.y = levels(yes)
}
X <- ifelse(cond,yes,no)
if (class.y == "factor") {
X = as.factor(X)
levels(X) = levels.y
} else {
class(X) <- class.y
}
return(X)
}
Run Code Online (Sandbox Code Playgroud)
顺便说一句:ifelse糟透了......强大的功能带来了很大的责任,即1x1矩阵和/或数字的类型转换(例如当它们应该被添加时)对我来说是好的,但ifelse中的这种类型转换显然是不需要的.我现在碰到多次ifelse的'bug',它只是在偷我的时间:-(
FW
@ fabian-werner提供的答案很棒,但是对象可以有多个类,而"factor"可能不一定是返回的第一个class(yes)
,所以我建议这个小修改来检查所有类属性:
safe.ifelse <- function(cond, yes, no) {
class.y <- class(yes)
if ("factor" %in% class.y) { # Note the small condition change here
levels.y = levels(yes)
}
X <- ifelse(cond,yes,no)
if ("factor" %in% class.y) { # Note the small condition change here
X = as.factor(X)
levels(X) = levels.y
} else {
class(X) <- class.y
}
return(X)
}
Run Code Online (Sandbox Code Playgroud)
我还向R开发团队提交了一个请求,要求添加一个记录的选项,让base :: ifelse()根据用户选择要保留的属性来保留属性.该请求是在这里:https://bugs.r-project.org/bugzilla/show_bug.cgi?id=16609 -它已经,理由是它一直是现在的方式标记为"WONTFIX"但我提供了一个后续论点,说明为什么简单的添加可能会为很多R用户带来麻烦.也许你的错误线程中的"+1"将鼓励R Core团队重新审视.
编辑:这是一个更好的版本,允许用户指定要保留的属性,"cond"(默认ifelse()行为),"是",根据上面代码的行为,或"否",对于"no"值的属性更好:
safe_ifelse <- function(cond, yes, no, preserved_attributes = "yes") {
# Capture the user's choice for which attributes to preserve in return value
preserved <- switch(EXPR = preserved_attributes, "cond" = cond,
"yes" = yes,
"no" = no);
# Preserve the desired values and check if object is a factor
preserved_class <- class(preserved);
preserved_levels <- levels(preserved);
preserved_is_factor <- "factor" %in% preserved_class;
# We have to use base::ifelse() for its vectorized properties
# If we do our own if() {} else {}, then it will only work on first variable in a list
return_obj <- ifelse(cond, yes, no);
# If the object whose attributes we want to retain is a factor
# Typecast the return object as.factor()
# Set its levels()
# Then check to see if it's also one or more classes in addition to "factor"
# If so, set the classes, which will preserve "factor" too
if (preserved_is_factor) {
return_obj <- as.factor(return_obj);
levels(return_obj) <- preserved_levels;
if (length(preserved_class) > 1) {
class(return_obj) <- preserved_class;
}
}
# In all cases we want to preserve the class of the chosen object, so set it here
else {
class(return_obj) <- preserved_class;
}
return(return_obj);
} # End safe_ifelse function
Run Code Online (Sandbox Code Playgroud)
小智 5
这不起作用的原因是因为,ifelse()函数将值转换为因子.一个很好的解决方法是在评估之前将其转换为字符.
dates <- as.Date(c('2011-01-01','2011-01-02','2011-01-03','2011-01-04','2011-01-05'))
dates_new <- dates - 1
dates <- as.Date(ifelse(dates =='2011-01-01',as.character(dates_new),as.character(dates)))
Run Code Online (Sandbox Code Playgroud)
除了基础R之外,这不需要任何库.
归档时间: |
|
查看次数: |
25694 次 |
最近记录: |