Sequentially remove vector elements - r

I want to replicate a vector with one value within this vector is missing (sequentially).
For example, my vector is
value <- 1:7
First, the series is without 1, second without 2, and so on. In the end, the series is in one vector.
The intended output looks like
2 3 4 5 6 7 1 3 4 5 6 7 1 2 4 5 6 7 1 2 3 5 6 7 1 2 3 4 6 7 1 2 3 4 5 6
Is there any smart way to do this?

You could use the diagonal matrix to set up a logical vector, using it to remove the appropriate values.
n <- 7
rep(1:n, n)[!diag(n)]
# [1] 2 3 4 5 6 7 1 3 4 5 6 7 1 2 4 5 6 7 1 2 3 5 6 7 1 2 3 4 6 7 1 2 3 4 5
# [36] 7 1 2 3 4 5 6

Well, you can certainly do it as a one-liner but I am not sure it qualifies as smart. For example:
x <- 1:7
do.call("c", lapply(as.list(-1:-length(x)), function(a)x[a]))
This simple uses lapply to create a list of copies of x with each of its entries deleted, and then concatenates them using c. The do.call function applies its first argument (a function) to its second argument (a list of arguments to the function).

For fun, it's also possible to just use rep:
> n <- 7
> rep(1:n, n)[rep(c(FALSE, rep(TRUE, n)), length.out=n^2)]
[1] 2 3 4 5 6 7 1 3 4 5 6 7 1 2 4 5 6 7 1 2 3 5 6 7 1 2 3 4 6 7 1 2 3 4 5 7 1 2
[39] 3 4 5 6
But lapply is cleaner, I think.

You could also do:
n <- 7
rep(seq(n), n)[-seq(1,n*n,n+1)]
#[1] 2 3 4 5 6 7 1 3 4 5 6 7 1 2 4 5 6 7 1 2 3 5 6 7 1 2 3 4 6 7 1 2 3 4 5 7 1 2 3 4 5 6

Related

Generating an vector with rep and seq but without the c() function [duplicate]

This question already has answers here:
R repeating sequence add 1 each repeat
(2 answers)
Closed 5 months ago.
Suppose that I am not allowed to use the c() function.
My target is to generate the vector
"1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9"
Here is my attempt:
rep(seq(1, 5, 1), 5)
# [1] 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
rep(0:4,rep(5,5))
# [1] 0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4
So basically I am sum them up. But I wonder if there is a better way to use rep and seq functions ONLY.
Like so:
1:5 + rep(0:4, each = 5)
# [1] 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9
I like the sequence option as well:
sequence(rep(5, 5), 1:5)
You could do
rep(1:5, each=5) + rep.int(0:4, 5)
# [1] 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9
Just to be precise and use seq as well:
rep(seq.int(1:5), each=5) + rep.int(0:4, 5)
(PS: You can remove the .ints, but it's slower.)
One possible way:
as.vector(sapply(1:5, `+`, 0:4))
[1] 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9
I would also propose the outer() function as well:
library(dplyr)
outer(1:5, 0:4, "+") %>%
array()
Or without magrittr %>% function in newer R versions:
outer(1:5, 0:4, "+") |>
array()
Explanation.
The first function will create an array of 1:5 by 0:4 sequencies and fill the intersections with sums of these values:
[,1] [,2] [,3] [,4] [,5]
[1,] 1 2 3 4 5
[2,] 2 3 4 5 6
[3,] 3 4 5 6 7
[4,] 4 5 6 7 8
[5,] 5 6 7 8 9
The second will pull the vector from the array and return the required vector:
[1] 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9

Transforming a looping factor variable into a sequence of numerics

I have a factor variable with 6 levels, which simplified looks like:
1 1 2 2 2 3 3 3 4 4 4 4 5 5 5 6 6 6 1 1 1 2 2 2 2... 1 1 1 2 2... (with n = 78)
Note, that each number is repeated mostly but not always three times.
I need to transform this variable into the following pattern:
1 1 2 2 2 3 3 3 4 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 8...
where each repetition of the 6 levels continuous counting ascending.
Is there any way / any function that lets me do that?
Sorry for my bad description!
Assuming that you have a numerical vector that represents your simplified version you posted. i.e. x = c(1,1,1,2,2,3,3,3,1,1,2,2), you can use this:
library(dplyr)
cumsum(x != lag(x, default = 0))
# [1] 1 1 1 2 2 3 3 3 4 4 5 5
which compares each value to its previous one and if they are different it adds 1 (starting from 1).
Maybe you can try rle, i.e.,
v <- rep(seq_along((v<-rle(x))$values),v$lengths)
Example with dummy data
x = c(1,1,1,2,2,3,3,3,4,4,5,6,1,1,2,2,3,3,3,4,4)
then we can get
> v
[1] 1 1 1 2 2 3 3 3 4 4 5 6 7 7 8 8 9 9
[19] 9 10 10
In base you can use diff and cumsum.
c(1, cumsum(diff(x)!=0)+1)
# [1] 1 1 2 2 2 3 3 3 4 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 8
Data:
x <- c(1,1,2,2,2,3,3,3,4,4,4,4,5,5,5,6,6,6,1,1,1,2,2,2,2)

Create set of matrices from concatenating columns of another matrix in r

I have two matrices A and B of dimension 5 by 3 and 5 by 2, respectively. I want to produce series of matrices combining each column of matrix B to A. The dimensions of the resulting matrices would be 5 by 4
Let A be
1 2 3
4 5 6
7 8 9
2 3 1
4 1 5
and B be
1 2
2 5
3 8
6 3
2 1
Then the resulting matrices are
1 2 3 1
4 5 6 2
7 8 9 3
2 3 1 6
4 1 5 2
and
1 2 3 2
4 5 6 5
7 8 9 8
2 3 1 3
4 1 5 1
Use our old friend the assignment operator. Assigning 1st column of B to 4th of A:
A[, 4] <- B[, 1]
> A
V1 V2 V3 V4
1 1 2 3 1
2 4 5 6 2
3 7 8 9 3
4 2 3 1 6
5 4 1 5 2
Then A[, 4] <- B[, 2], etc.

Keep column names after calling function combn

After looking at another post about column names and combn function here consider the same data.frame. We make a combn with all 2 possible vectors:
foo <- data.frame(x=1:5,y=4:8,z=10:14, w=8:4)
all_comb <- combn(foo,2)
Is there a way to keep column names after the combn call so in this case we could get "x y" instead of "X1.5 X4.8" as shown below ?
comb_df <- data.frame(all_comb[1,1],all_comb[2,1])
print(comb_df)
X1.5 X4.8
1 1 4
2 2 5
3 3 6
4 4 7
5 5 8
I suspect you really want to use expand.grid() instead.
Try this:
head(expand.grid(foo))
x y z w
1 1 4 10 8
2 2 4 10 8
3 3 4 10 8
4 4 4 10 8
5 5 4 10 8
6 1 5 10 8
or
head(expand.grid(foo[, 1:2]))
x y
1 1 4
2 2 4
3 3 4
4 4 4
5 5 4
6 1 5

Using paste in R

I have a question regarding the use of paste in R
a<-c(1,2,3,5,5,6,7,8)
b<-c(2,3,5,6,2,3,6,7)
d<-c(2,8,4,6,3,7,3,5)
df<-data.frame(a,b)
cbind(df,sugar=d)
Using the above code, I got this:
> a b sugar
1 1 2 2
2 2 3 8
3 3 5 4
4 5 6 6
5 5 2 3
6 6 3 7
7 7 6 3
8 8 7 5
However, I wonder why I couldn't get the same results using paste function:
name<-c("sugar","salt","fat")
cbind(df,paste(name[1])=d)
Any help would be much appreciated!!
If you need to create a new column with name stored in an object, try
df[name[1]] <- d
df
# a b sugar
#1 1 2 2
#2 2 3 8
#3 3 5 4
#4 5 6 6
#5 5 2 3
#6 6 3 7
#7 7 6 3
#8 8 7 5
Another option might be to use assign
assign('df', `[[<-`(df, name[1], value=d))
You want to change the name, so try setNames.
> setNames(cbind(df, d), c(colnames(df),name[1]))
a b sugar
1 1 2 2
2 2 3 8
3 3 5 4
4 5 6 6
5 5 2 3
6 6 3 7
7 7 6 3
8 8 7 5

Resources