以下是生成一些示例数据的代码:
set.seed(1000)
dat <- data.frame(A = runif(5, -5, 5),
B = runif(5, -5, 5),
C = runif(5, -5, 5))
数据:
A B C
1 -1.721213 -4.3226204 -1.500625
2 2.588465 2.3871486 2.554616
3 -3.860636 0.8353505 -1.829158
4 1.907551 -2.8422860 3.658128
5 0.164024 -2.4387760 2.641607
我想比较每一列并返回TRUE
或FALSE
,具体取决于我们按行排列是否有负值或正值。例如,在A列和B列的第4行中,我们将获得TRUE
。因为一个是积极的而另一个是消极的。如果它被交换(该行的A为负值,但B为正值),那么它也会返回TRUE
。
但是,如果要比较的两个值中的任何一个在1到-1之间,则不会进行比较,只会返回NA
。以下是此示例的最终输出结果:
A B C AB BC AC
1 -1.721213 -4.3226204 -1.500625 FALSE FALSE FALSE
2 2.588465 2.3871486 2.554616 FALSE FALSE FALSE
3 -3.860636 0.8353505 -1.829158 NA NA FALSE
4 1.907551 -2.8422860 3.658128 TRUE TRUE FALSE
5 0.164024 -2.4387760 2.641607 NA TRUE NA
我尝试使用这种逻辑来比较行:
if((dat$A > 1 & datB < -1) | (dat$A < -1 & dat$B > -1) == TRUE)
...
但我认为必须有一种更有效的方法。
这里有许多很棒的答案,当我测试它们时,它们都有效。由于其可读性和简洁性,我最喜欢mpalanco的答案。但是,我选择了DMC的答案,因为它可以推广到其他案例,对于寻找这个或类似问题答案的其他人可能会有用。
答案 0 :(得分:2)
最好使用一些功能:
is_between <- function(x, a, b) {
x > a & x < b
}
makeCol <- function(col1, col2) {
ifelse(
is_between(col1, -1, 1) | is_between(col2, -1, 1),
NA,
!as.logical(sign(col1) + sign(col2))
)
}
dat$AB <- makeCol(dat$A, dat$B)
dat$BC <- makeCol(dat$B, dat$C)
dat$AC <- makeCol(dat$A, dat$C)
为了让这个更干,你可以在他的评论中关注@ akrun的主角并做类似的事情:
combn(seq_along(dat), 2, function(x) { makeCol(dat[, x[1]], dat[, x[2]]) })
答案 1 :(得分:2)
如果您的列数多于{A, B, C}
而且您希望查看所有列对,那么这个结果会进行推广:
library(tidyr)
library(dplyr)
# get original names
orig_names <- names(dat)
# add a row indicator
dat <- dat %>% mutate(k = row_number())
dat2 <- dat %>%
# reshape to long
gather(letter, value, A:C) %>%
# change value to {-1, 1}
mutate(
value = ifelse(value <= -1, -1, ifelse(value >= 1, 1, NA)),
letter = as.character(letter)
)
# create a placeholder data frame for result
d_new <- expand.grid(
V1 = orig_names,
V2 = orig_names,
k = 1:nrow(dat),
stringsAsFactors = FALSE
) %>%
filter(V1 < V2)
# compute result
result <- d_new %>%
left_join(dat2, by = c("V1" = "letter", "k" = "k")) %>%
left_join(dat2, by = c("V2" = "letter", "k" = "k")) %>%
mutate(
result = (value.x != value.y),
combo = paste0(V1, V2)
) %>%
select(-starts_with("value"), -V1, -V2) %>%
spread(combo, result)
# join with original data
dat %>% left_join(result)
## A B C k AB AC BC
## 1 -1.721213 -4.3226204 -1.500625 1 FALSE FALSE FALSE
## 2 2.588465 2.3871486 2.554616 2 FALSE FALSE FALSE
## 3 -3.860636 0.8353505 -1.829158 3 NA FALSE NA
## 4 1.907551 -2.8422860 3.658128 4 TRUE FALSE TRUE
## 5 0.164024 -2.4387760 2.641607 5 NA NA TRUE
答案 2 :(得分:1)
这将是我的解决方案。
dat[dat$A*dat$B<0 & abs(dat$A)>=1 & abs(dat$B)>=1,4]<-TRUE
dat[dat$A*dat$B>0 & abs(dat$A)>=1 & abs(dat$B)>=1,4]<-FALSE
dat[dat$C*dat$B<0 & abs(dat$C)>=1 & abs(dat$B)>=1,5]<-TRUE
dat[dat$C*dat$B>0 & abs(dat$C)>=1 & abs(dat$B)>=1,5]<-FALSE
dat[dat$A*dat$C<0 & abs(dat$A)>=1 & abs(dat$C)>=1,6]<-TRUE
dat[dat$A*dat$C>0 & abs(dat$A)>=1 & abs(dat$C)>=1,6]<-FALSE
colnames(dat)<-c("A","B","C","AB","BC","AC")
答案 3 :(得分:1)
一个非常基本的一步一步的方法:
# To keep original data
dat2 <- dat
# Assign NA to values between -1 and -1
dat[ifelse(dat >-1 & dat < 1, TRUE, FALSE)] <- NA
# Create three new columns
dat[4:6] <- data.frame(AB = dat$A*dat$B, BC = dat$B*dat$C, AC = dat$A*dat$C)
# If the are positive will be FALSE
dat[4:6] <- ifelse(dat[, 4:6] > 0, FALSE, TRUE)
# Final result
cbind(dat2[, 1:3], dat[, 4:6])
输出:
A B C AB BC AC
1 -1.721213 -4.3226204 -1.500625 FALSE FALSE FALSE
2 2.588465 2.3871486 2.554616 FALSE FALSE FALSE
3 -3.860636 0.8353505 -1.829158 NA NA FALSE
4 1.907551 -2.8422860 3.658128 TRUE TRUE FALSE
5 0.164024 -2.4387760 2.641607 NA TRUE NA