Replace the same occurances by multiple strings - vector

Let's say I have a vector with multiple strings:
a<- c('a?cd','ab?cd','abc?')
How can I replace the first "?" by b the second "?" by c and the third "?" by d, in order to produce a result like this:
'abcd','abcd','abcd'
Improving the topic with the answer from G. Grothendieck!
In case we have two symbols in the same element that should be replaced by different patterns:
a <- c('espa?a','per? an?n','peque?os')
L <- c('N','U','O','N');
fmt <- gsub("[?]", "%s", a)
g <- cumsum(sequence(nchar(gsub("[^?]", "", a)))==1)
mapply(function(fmt, x) do.call("sprintf", as.list(c(fmt, x))), fmt, split( L, g), USE.NAMES = FALSE)

Apply chartr across each component as follows. Note that head(...) is c("b", "c", "d") . No packages are used.
a<- c('a?cd','ab?cd','abc?') # test input
mapply(chartr, "?", head(letters[-1], length(a)), a, USE.NAMES = FALSE)
## [1] "abcd" "abccd" "abcd"
If what you meant was to check if any elements of "a", "b", "c", "d" are missing from each component and if so then replace ? with that missing element then first create a list of L of replacements and then apply sub to each component with it. We assume that there are 0 or 1 missing elements from each component and 0 or 1 instances of ? in each component. Again, no packages are used.
L <- lapply(strsplit(a, ""), setdiff, x = letters[1:4])
L[lengths(L) == 0] <- ""
mapply(`sub`, "[?]", L, a, USE.NAMES = FALSE)
## [1] "abcd" "abcd" "abcd"

stringr::str_replace() has vectorized replacement so you can do:
library(stringr)
str_replace(a, "\\?", letters[seq_along(a) + 1])
[1] "abcd" "abccd" "abcd"

You can use str_replace from stringrpackage
library(stringr)
a<- c('a?cd','ab?cd','abc?')
str_replace(a,"[?]",letters[2:4])
[1] "abcd" "abccd" "abcd"
or
str_replace(a, "[?]", c("b", "c", "d"))
[1] "abcd" "abccd" "abcd"

Related

How to concatenate vectors of different lengths without recycling and without using a loop?

Let's say I have two vectors of different lengths:
v1 <- c("A", "B", "C", "D", "E")
v2 <- c("F", "G", "H")
I want to concatenate these two vectors. But I don't want to recycle the short vector. I just want NAs for the missing elements. So I want this:
[1] "A-F" "B-G" "C-H" "D-NA" "E-NA"
If I use paste, the short vector gets recycled:
paste(v1, v2, sep = "-")
# [1] "A-F" "B-G" "C-H" "D-F" "E-G"
I could do it easily with a loop:
v3 <- c()
for (i in seq_along(v1)) {
v3[i] <- paste(v1[i], v2[i], sep = "-")
}
v3
# [1] "A-F" "B-G" "C-H" "D-NA" "E-NA"
But if anyone on StackOverflow discovered I was extending a vector dynamically inside a loop, they would have a conniption.
So, how can I concatenate two vectors of different lengths without recycling and without using a loop?
Find the length of the longest vector and set the length of each vector to that, then concatenate.
v1 <- c("A", "B", "C", "D", "E")
v2 <- c("F", "G", "H")
n <- max(length(v1), length(v2))
length(v1) <- n
length(v2) <- n
paste(v1, v2, sep = "-")
#> [1] "A-F" "B-G" "C-H" "D-NA" "E-NA"
Created on 2021-09-26 by the reprex package (v2.0.1)
We could create a list. Add NA's to make equal length. then paste the elements of the list:
my_list <- list(v1, v2)
my_list1 <- lapply(my_list, `length<-`, max(lengths(my_list)))
paste(my_list1[[1]], my_list1[[2]], sep="-")
[1] "A-F" "B-G" "C-H" "D-NA" "E-NA"
We can also use
do.call(paste, c(as.data.frame(stringi::stri_list2matrix(list(v1, v2))), sep="-"))
[1] "A-F" "B-G" "C-H" "D-NA" "E-NA"
You could complete the shorter vector with "NA"
v1 <- c("A", "B", "C", "D", "E")
v2 <- c("F", "G", "H")
n_v1 <- length(v1)
n_v2 <- length(v2)
if(n_v1 > n_v2){
paste(v1,c(v2,rep("NA",(n_v1-n_v2))),sep = "-")
}
[1] "A-F" "B-G" "C-H" "D-NA" "E-NA"

Non consecutive combinations of array elements in R

I want to generate all the possible combinations of nonadjacent elements in an array.
For example:
array_a <- c("A","B","C")
possible combinations would be : AC and CA
How can I implement this in R?
If nonadjacent elements are defined as elements with distance greater than one in absolute values, then one option could be:
mat <- which(as.matrix(dist(seq_along(array_a))) > 1, arr.ind = TRUE)
paste0(array_a[mat[, 1]], array_a[mat[, 2]])
[1] "CA" "DA" "EA" "DB" "EB" "AC" "EC" "AD" "BD" "AE" "BE" "CE"
Sample data:
array_a <- c("A", "B", "C", "D", "E")
We can use outer
c(outer(array_a, array_a, FUN = paste, sep=""))
Or if we want to omit alternate elements
outer(array_a[c(TRUE, FALSE)], array_a[c(TRUE, FALSE)], FUN = paste, sep="")
Or using crossing
library(dplyr)
library(tidyr)
crossing(v1 = array_a[c(TRUE, FALSE)],
v2 = array_a[c(TRUE, FALSE)]) %>%
filter(v1 != v2) %>%
unite(v1, v1, v2, sep="") %>%
pull(v1)
#[1] "AC" "CA"
NOTE: It is not clear about the assumptions for non-adjacent elements. We answered it based on a different assumption.
Another base R option using expand.grid + subset
inds <- subset(expand.grid(seq_along(array_a), seq_along(array_a)), abs(Var1 - Var2) > 1)
paste0(array_a[inds$Var1],array_a[inds$Var2])
The #tmfmnk solution is so cool. Still I want to add sth from me.
I use the arrangements package for permutations without repetition.
array_a <- c("A", "B", "C", "D", "E")
#vec to rm from permutations neighbors
vec = paste0(array_a[-1], head(array_a, -1))
cc = apply(arrangements::permutations(array_a, 2, replace = F), 1, function(x) paste0(x, collapse = ""))
> setdiff(cc, c(vec, stringi::stri_reverse(vec)))
[1] "AC" "AD" "AE" "BD" "BE" "CA" "CE" "DA" "DB" "EA" "EB" "EC"

unlist to produce a vector of same length

I have a list like this:
lst <- list(a = c("y"), b = c("A", "B", "C"), c = c("x1", "x2"))
lst
> lst
$a
[1] "y"
$b
[1] "A" "B" "C"
$c
[1] "x1" "x2"
If I unlist it, I get:
unlist(lst)
> unlist(lst)
a b1 b2 b3 c1 c2
"y" "A" "B" "C" "x1" "x2"
How can I get a vector like:
a b c
"y" "A, B, C" "x1, x2"
Edit:
A similar question Convert a list of lists to a character vector was answered previously. The answer proposed by #42_ sapply( l, paste0, collapse="") could be used with a small modification: sapply( l, paste0, collapse=", "). Ronak Shah's sapply(lst, toString) to my question is a little more intuitive.
We can use toString to collapse all the elements in every list into a comma-separated string.
sapply(lst, toString)
# a b c
# "y" "A,B,C" "x1,x2"
which is same as using paste with collapse argument as ","
sapply(lst, paste, collapse = ",")
You can also do
unlist(Map(function(x) paste0(x,collapse = ","),lst))
Or
unlist(lapply(lst,function(x) paste0(x,collapse = ",")))
Or use purrr package
purrr::map_chr(lst,paste0,collapse = ",")
we can use map
library(purrr)
library(stringr)
map_chr(lst, str_c, collapse=",")

Replace one element in vector with multiple elements

I have a vector where I want to replace one element with multiple element, I am able to replace with one but not multuiple, can anyone help?
For example I have
data <- c('a', 'x', 'd')
> data
[1] "a" "x" "d"
I want to replace "x" with "b", "c" to get
[1] "a" "b" "c" "d"
However
gsub('x', c('b', 'c'), data)
gives me
[1] "a" "b" "d"
Warning message:
In gsub("x", c("b", "c"), data) :
argument 'replacement' has length > 1 and only the first element will
be used
Here's how I would tackle it:
data <- c('a', 'x', 'd')
lst <- as.list(data)
unlist(lapply(lst, function(x) if(x == "x") c("b", "c") else x))
# [1] "a" "b" "c" "d"
We're making use of the fact that list-structures are more flexible than atomic vectors. We can replace a length-1 list-element with a length>1 element and then unlist the result to go back to an atomic vector.
Since you want to replace exact matches of "x" I prefer not to use sub/gsub in this case.
You may try this , although I believe the accepted answer is great:
unlist(strsplit(gsub("x", "b c", data), split = " "))
Logic: Replacing "x" with "b c" with space and then doing the strsplit, once its splitted we can convert is again back to vector using unlist.
This is a bit tricky of a problem because in your replacement you also want to grow your vector. That being said, I believe this should work:
replacement <- c("b","c")
new_data <- rep(data, times = ifelse(data=="x", length(replacement), 1))
new_data[new_data=="x"] <- replacement
new_data
#[1] "a" "b" "c" "d"
This will also work if you have multiple "x"s in your vector like:
data <- c("a","x","d","x")
Another approach:
data <- c('a', 'x', 'd')
pattern <- "x"
replacement <- c("b", "c")
sub_one_for_many <- function(pattern, replacement, x) {
removal_index <- which(x == pattern)
if (removal_index > 1) {
result <- c(x[1:(removal_index-1)], replacement, x[(removal_index+1):length(x)])
} else if (removal_index == 1) {
result <- c(replacement, x[2:length(x)])
}
return(result)
}
answer <- sub_one_for_many(pattern, replacement, data)
Output:
> answer
[1] "a" "b" "c" "d"

How do concat a vector of character in R?

I tried using the paste command but it returns the same vector?
x = c("a","b","c")
y = paste(x)
y
[1] "a" "b" "c"
length(y)
[1] 3
I want a single character of "abc"
The collapse="" options is your friend:
> x <- c("a", "b", "c")
> paste(x, collapse="")
[1] "abc"
>
[ There is still no rstats tag here. ]

Resources