In R, how to replace values in multiple columns with a vector of values equal to the same width?

*爱你&永不变心* 提交于 2019-12-23 10:44:31

问题


I am trying to replace every row's values in 2 columns with a vector of length 2. It is easier to show you.

First here is a some data.

set.seed(1234) 
x<-data.frame(x=sample(c(0:3), 10, replace=T))
x$ab<-0 #column that will be replaced
x$cd<-0 #column that will be replaced

The data looks like this:

   x ab cd
1  0  0  0
2  2  0  0
3  2  0  0
4  2  0  0
5  3  0  0
6  2  0  0
7  0  0  0
8  0  0  0
9  2  0  0
10 2  0  0

Every time x=2 or x=3, I want to ab=0 and cd=1.

My attempt is this:

x[with(x, which(x==2|x==3)), c(2:3)] <- c(0,1)

Which does not have the intended results:

   x ab cd
1  0  0  0
2  2  0  1
3  2  1  0
4  2  0  1
5  3  1  0
6  2  0  1
7  0  0  0
8  0  0  0
9  2  1  0
10 2  0  1

Can you help me?


回答1:


The reason it doesn't work as you want is because R stores matrices and arrays in column-major layout. And when you a assign a shorter array to a longer array, R cycles through the shorter array. For example if you have

x<-rep(0,20)
x[1:10]<-c(2,3)

then you end up with

 [1] 2 3 2 3 2 3 2 3 2 3 0 0 0 0 0 0 0 0 0 0

What is happening in your case is that the sub-array where x is equal to 2 or 3 is being filled in column-wise by cycling through the vector c(0,1). I don't know of any simple way to change this behavior.

Probably the easiest thing to do here is simply fill in the columns one at a time. Or, you could do something like this:

indices<-with(x, which(x==2|x==3))
x[indices,c(2,3)]<-rep(c(0,1),each=length(indices))



回答2:


Another alternative: Using a data.table, this is a one-liner:

require(data.table)
DT <- data.table(x)
DT[x%in%2:3,`:=`(ab=0,cd=1)]

Original answer: You can pass a matrix of row-column pairs:

ijs <- expand.grid(with(x, which(x==2|x==3)),c(2:3))
ijs <- ijs[order(ijs$Var1),]

x[as.matrix(ijs)] <- c(0,1)

which yields

   x ab cd
1  0  0  0
2  2  0  1
3  2  0  1
4  2  0  1
5  3  0  1
6  2  0  1
7  0  0  0
8  0  0  0
9  2  0  1
10 2  0  1

My original answer worked on my computer, but not a commenter's.




回答3:


Generalized for multi-columns and multi-values:

mycol<-as.list(names(x)[-1])
myvalue<-as.list(c(0,1))
kk<-Map(function(y,z) list(x[x[,1] %in% c(2,3),y]<-z,x),mycol, myvalue)
myresult<-data.frame(kk[[2]][[2]])


x ab cd
1  1  0  0
2  1  0  0
3  0  0  0
4  0  0  0
5  0  0  0
6  3  0  1
7  2  0  1
8  3  0  1
9  3  0  1
10 0  0  0



回答4:


You could use ifelse:

> set.seed(1234) 
> dat<-data.frame(x=sample(c(0:3), 10, replace=T))
> dat$ab <- 0 
> dat$cd <- ifelse(dat$x==2 | dat$x==3, 1, 0)

   x ab cd
1  0  0  0
2  2  0  1
3  2  0  1
4  2  0  1
5  3  0  1
6  2  0  1
7  0  0  0
8  0  0  0
9  2  0  1
10 2  0  1



回答5:


What about that?

 x[x$x%in%c(2,3),c(2,3)]=matrix(rep(c(0,1),sum(x$x%in%c(2,3))),ncol=2,byrow=TRUE)



回答6:


x$ab[x$x==2 | x$x==3] <- 0
x$cd[x$x==2 | x$x==3] <- 1

EDIT

Here is a general approach that would work with lots of columns. You simply create a vector of the replacement values you wish to use for each column.

set.seed(1234) 
y<-data.frame(x=sample(c(0:3), 10, replace=T))
y$ab<-4 #column that will be replaced
y$cd<-2 #column that will be replaced
y$ef<-0 #column that will be replaced
y

#   x ab cd ef
#1  0  4  2  0
#2  2  4  2  0
#3  2  4  2  0
#4  2  4  2  0
#5  3  4  2  0
#6  2  4  2  0
#7  0  4  2  0
#8  0  4  2  0
#9  2  4  2  0
#10 2  4  2  0

replacement.values <- c(10,20,30)

y2 <- y
y2[,2:ncol(y)] <- sapply(2:ncol(y), function(j) { 
                    apply(y, 1, function(i) {
                      ifelse((i[1] %in% c(2,3)), replacement.values[j-1], i[j])
                    })
                  })
y2

#   x ab cd ef
#1  0  4  2  0
#2  2 10 20 30
#3  2 10 20 30
#4  2 10 20 30
#5  3 10 20 30
#6  2 10 20 30
#7  0  4  2  0
#8  0  4  2  0
#9  2 10 20 30
#10 2 10 20 30


来源:https://stackoverflow.com/questions/18986932/in-r-how-to-replace-values-in-multiple-columns-with-a-vector-of-values-equal-to

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!