Let's say I have 2 lists
divisor = c(0, 1, 1, 7, 7, 8, 8, 8, 9 )
remainder = c(99, 0, 1, 1, 99, 0, 1, 99, 0)
I want a divisor element to be element + 1 if its corresponding remainder is NOT 0.
The final answer should look like:
updated.divisor = (1, 1, 2, 8, 8, 8, 9, 9, 9)
How would I do this using sapply?
So far I have
sapply(remainder, function(x) {
if x != 0{
#divisor = divisor + 1
}
else{
#divisor = divisor + 0
}
}
P.S. I could probably use a nested loop but I want to be able to do this using sapply.
You don't need a loop:
divisor + (remainder!=0)
[1] 1 1 2 8 8 8 9 9 9
This is one of the most fundamental principles of R: all basic operations (and many functions) accept vectors as input and perform the operation on all elements of that vector at the same time.
To your comment: If you wanted an apply type solution you would use mapply because it allows you to process two arguments "alongside each other":
mapply( function(x,y) {x + !(y==0)}, x=divisor, y=remainder)
#[1] 1 1 2 8 8 8 9 9 9
An ifelse solution would make sense, too:
ifelse(remainder !=0, divisor+1, divisor)
#[1] 1 1 2 8 8 8 9 9 9
Related
How is it possible to transform the following vector:
x <- c(0, 0, 0, 1, 0, 3, 2, 0, 0, 0, 5, 0, 0, 0, 8)
into the desired form:
y <- c(1, 1, 1, 1, 3, 3, 2, 5, 5, 5, 5, 8, 8, 8, 8)
Any idea would be highly appreciated.
Here's another approach using only base R:
idx <- x != 0
split(x, cumsum(idx) - idx) <- x[idx]
The x-vector is now:
x
#[1] 1 1 1 1 3 3 2 5 5 5 5 8 8 8 8
you can use zoo to fill NAs via na.locf function as follows,
zoo::na.locf(replace(x, x==0, NA), fromLast = TRUE)
#[1] 1 1 1 1 3 3 2 5 5 5 5 8 8 8 8
Using rle, you can do the following in base R.
tmp <- rle(x)
tmp$values[which(tmp$values == 0)] <- tmp$values[which(tmp$values == 0) + 1L]
inverse.rle(tmp)
[1] 1 1 1 1 3 3 2 5 5 5 5 8 8 8 8
Note that this assumes the final value is not 0. If this is not the case, you could use head(which(tmp$values == 0), -1) in place of which(tmp$values == 0) to drop the final value.
Say we have the following:
a=c( 1, 9, 5, 7, 8, 11)
length(a) ## 6
and I want to obtain:
a_desired=c( 1, 1, 9, 9, 5, 5, 7, 7, 8, 11)
length(a_desired) ## 10
Basically it stops replicating when it reaches the desired length, in this case 10.
If the desired length is 14,
a_desired=c( 1, 1, 1, 9, 9, 9, 5, 5, 7, 7, 8, 8, 11, 11)
Does anyone have a suggestion on how to obtain this or perhaps a link on something similar asked before ?(I'm not too sure what keyword to look for)
You could write your own function to do something like this
extend_to <- function(x, len) {
stopifnot(len>0)
times = len %/% length(x)
each <- rep(times, length(x))
more <- len-sum(each)
if (more>0) {
each[1:more] <- each[1:more]+1
}
rep(x, each)
}
a <- c( 1, 9, 5, 7, 8, 11)
extend_to(a, 6)
# [1] 1 9 5 7 8 11
extend_to(a, 10)
# [1] 1 1 9 9 5 5 7 7 8 11
extend_to(a, 14)
# [1] 1 1 1 9 9 9 5 5 7 7 8 8 11 11
extend_to(a, 2)
# [1] 1 9
We use the rep() to repeat each element a certain number of times.
So if your sequence is currently of length M and you want length N > M, then you have these possibilities:
N <= 2M: double the first (N-M) items
2M < N <= 3M: triple the first (N-2M) items, double the rest
3M < N <= 4M: quadruple the first (N-3M) items, triple the rest.
and so on.
So first, divide the target length by the current length, take the floor, and replicate the sequence that many times. Then add an extra copy of the first remainder items.
a=c( 1, 9, 5, 7, 8, 11)
m=length(a)
n=10 # desired new length
new_a = append(
rep(a[1:(n%%m)],each=ceiling(n/m)),
rep(a[((n%%m)+1):m],each=floor(n/m)))
I have a matrix, which represents mobility between various jobs:
jobnames <- c("job 1","job 2","job 3","job 4","job 5","job 6","job 7")
jobdat <- matrix(c(
5, 5, 5, 0, 0, 5, 5,
5, 5, 2, 5, 5, 1, 5,
1, 5, 5, 5, 0, 0, 1,
1, 0, 5, 5, 8, 0, 1,
0, 5, 0, 0, 5, 5, 1,
0, 0, 5, 5, 0, 5, 5,
0, 1, 0, 0, 5, 1, 5
),
nrow = 7, ncol = 7, byrow = TRUE,
dimnames = list(jobnames,jobnames
))
This is treated as a directed, weighted adjacency matrix in a social network analysis. The direction of the network is from rows to columns: So mobility is defined as going from a job-row to a job-column. The diagonal is relevant, since it is possible to change to the same job in another firm.
I need to collapse this matrix according to a prefigured list
containing the index of the jobs that should be combined:
group.list <- list(grp1=c(1,2) ,grp2 =c(3,4))
Now, since it is an adjacency matrix, it's a bit different than the other ' answers about how to collapse a matrix that I've ' found here and elsewhere. The collapse has to be simultanious on both the rows and the columns. And some jobs isn't grouped at all. So the result in this example should be like this:
group.jobnames <- c("job 1 and 2","job 3 and 4","job 5","job 6","job 7")
group.jobdat <- matrix(c(
20,12,5,6,10,
7,17,8,0,2,
5,0,5,5,1,
0,10,0,5,5,
1,0,5,1,5
),
nrow = 5, ncol = 5, byrow = TRUE,
dimnames = list(group.jobnames,group.jobnames
))
This example groups the two first jobs and then the next two, but in my actual data it could be any combination of (indexes of) jobs, and any number of jobs in each group. So job [1,7] could be one group, and job [2,3,6] could be another group, while job 4 or 5 wasn't grouped. Or any other combination.
Thank you for your time,
I believe there are some typos in the intended output, and the group.list definition. If I am correct in my interpretation, here is a solution.
Here is a new group.list to conform with the names of the desired output. In this version, group 2 is mapped to 1 and group 4 is mapped to 3, which conforms with the text in group.jobs.
group.list <- list(grp1=c(1, 3), grp2=c(2, 4))
Given this list, construct a grouping vector
# initial grouping
groups <- seq_len(ncol(jobdat))
# map elements of second list item to values of first list item
groups[match(group.list[["grp2"]], groups)] <- group.list[["grp1"]]
groups
[1] 1 1 3 3 5 6 7
So, now groups 1 and 2 are the same as well as 3 and 4. Now, we use rowsum and a couple of transposes to calculate the output.
myMat <- t(rowsum(t(rowsum(jobdat, groups)), groups))
# add the group names
dimnames(myMat) <- list(group.jobnames,group.jobnames)
myMat
job 1 and 2 job 3 and 4 job 5 job 6 job 7
job 1 and 2 20 12 5 6 10
job 3 and 4 7 20 8 0 2
job 5 5 0 5 5 1
job 6 0 10 0 5 5
job 7 1 0 5 1 5
In response to the OP's comments below, the grouping was intended to be within list elements, rather than corresponding positions between list elements as I had originally interpreted. To accomplish this form a grouping, a repeated feeding of replace to Reduce will accomplish the task.
With group.list as in the question,
group.list <- list(grp1=c(1, 2), grp2=c(3, 4))
groups <- Reduce(function(x, y) replace(x, x[x %in% y], min(y)),
c(list(groups), unname(group.list)))
groups
[1] 1 1 3 3 5 6 7
Here, replace takes the original grouping, finds the elements in the grouping that are in one of the vectors in group.list, and replaces these with the minimum value of that vector. The Reduce function repeatedly applies this operation on the original group variable, except modifying it in each iteration.
With this result, we use the above transposes and rowsum to get
myMat
job 1 and 2 job 3 and 4 job 5 job 6 job 7
job 1 and 2 20 12 5 6 10
job 3 and 4 7 20 8 0 2
job 5 5 0 5 5 1
job 6 0 10 0 5 5
job 7 1 0 5 1 5
I have 10 vectors (v_1 to v_10) and I need all of them multiplied with another vector v_mult (i.e. v_1*v_mult, v_2*v_mult etc.). How to I solve this problem within a for-loop? Im stuck to the loop-solution (which I do not find) because it is part of a larger analysis.
v_10<-c(2, 3, 5, 8)
v_20<-c(3, 9, 0, 1)
v_30<-c(15, 9, 6, 0)
v_40<-c(4, 9, 6, 1)
v_50<-c(1, 7, 3, 9)
v_60<-c(5, 9, 5, 1)
v_70<-c(5, 8, 2, 6)
v_80<-c(5, 8, 1, 6)
v_90<-c(5, 0, 1, 6)
v_10<-c(2, 8, 1, 0)
v_mult<-c(8, 5, 1, 9)
Those vectors should be all together in a matrix:
vlist <- mget(ls(pattern = "v_[[:digit:]*]"))
m <- do.call(cbind, vlist)
m * v_mult
# v_10 v_20 v_30 v_40 v_50 v_60 v_70 v_80 v_90
#[1,] 16 24 120 32 8 40 40 40 40
#[2,] 40 45 45 45 35 45 40 40 0
#[3,] 1 0 6 6 3 5 2 1 1
#[4,] 0 9 0 9 81 9 54 54 54
You can of course extract each vector from the matrix using column subsetting, e.g., m[, "v_10"] or m[, 1].
We can get all the vector objects in a list using mgetand multiply each element of the list with 'v_mult' using Map.
Map('*',mget(paste('v', seq(10, 100, by=10), sep="_")), list(v_mult))
Or use set from data.table which would be very fast as it doesn't have the .[data.table overhead.
library(data.table)
DT <- setDT(mget(paste('v', seq(10, 100, by=10), sep="_")))
for(j in seq_along(DT)){
set(DT, i=NULL, j= j, value= DT[[j]]*v_mult)
}
I have a vector c(9,6,3,4,2,1,5,7,8), and I want to switch the elements at index 2 and at index 5 in the vector. However, I don't want to have to create a temporary variable and would like to make the switch in one call. How would I do that?
How about just x[c(i,j)] <- x[c(j,i)]? Similar to replace(...), but perhaps a bit simpler.
swtch <- function(x,i,j) {x[c(i,j)] <- x[c(j,i)]; x}
swtch(c(9,6,3,4,2,1,5,7,8) , 2,5)
# [1] 9 2 3 4 6 1 5 7 8
You could use replace().
x <- c(9, 6, 3, 4, 2, 1, 5, 7, 8)
replace(x, c(2, 5), x[c(5, 2)])
# [1] 9 2 3 4 6 1 5 7 8
And if you don't even want to assign x, you can use
replace(
c(9, 6, 3, 4, 2, 1, 5, 7, 8),
c(2, 5),
c(9, 6, 3, 4, 2, 1, 5, 7, 8)[c(5, 2)]
)
# [1] 9 2 3 4 6 1 5 7 8
but that's a bit silly. You will probably want x assigned to begin with.
If you actually want to do it without creating a temporary copy of the vector, you would need to write a short C function.
library(inline)
swap <- cfunction(c(i = "integer", j = "integer", vec="integer"),"
int *v = INTEGER(vec);
int ii = INTEGER(i)[0]-1, jj = INTEGER(j)[0]-1;
int tmp = v[ii];
v[ii] = v[jj];
v[jj] = tmp;
return R_NilValue;
")
vec <- as.integer(c(9,6,3,4,2,1,5,7,8))
swap(2L, 5L, vec)
vec
# [1] 9 2 3 4 6 1 5 7 8