确定两列是否在R中包含负值和正值

时间:2015-10-01 17:58:04

标签: r dataframe

以下是生成一些示例数据的代码:

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

我想比较每一列并返回TRUEFALSE,具体取决于我们按行排列是否有负值或正值。例如,在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的答案,因为它可以推广到其他案例,对于寻找这个或类似问题答案的其他人可能会有用。

4 个答案:

答案 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