Show a value inside a prompt in R - r

I have been searching this for some time and haven't been able to find the answer. Hope you can help me:
a <- readline(prompt="No. of attributes: ")
lev <- c()
i <- 0
while (i<a) {
l <- readline(prompt="No. of levels in attribute i: ")
l <- as.numeric(strsplit(l,",")[[1]])
lev <- c(lev, l)
i=i+1
}
Inside the loop, in the prompt in l, I want i to change by the real value of i.
Sorry for being such a noob.
Thank you!

You could use
prompt=paste0("No. of levels in attribute ", i, ":")
Edit: FYI, there's also a paste function that is very similar, but puts spaces between the strings it pastes. Also look into the collapse= parameter to paste and paste0 if you're trying to paste together a vector of strings.

Do you mean "assign the prompt value to 'i'"? Wouldn't this work?
(edited)
Seems like you need three changes --
use paste0 (which concatenates raw text with string variables, with "0" spaces, i.e., no spaces)
your need to assign to "l" to correctly so the prompt increments
You maybe want i to start at 1, and change the i<a to i<=a so you are correctly getting each level:
a <- readline(prompt="No. of attributes: ")
lev <- c()
i <- 1
while (i<=a) {
l <- readline(prompt=paste0("No. of levels in attribute ",i,": "))
l <- as.numeric(strsplit(l,",")[[1]])
lev <- c(lev, l)
i=i+1
}

Related

Using paste and sum inside a for-loop

I need to compare a character string to multiple others and tried to do it the following way:
empty = character(0)
ps_2 = c("h2","h3")
ps_3 = c("h3", "h4")
visible = ("h2")
i = 2
ps_t = empty
ps_t <- append(ps_t, sum(visible %in% paste("ps_", i, sep="")))
With the intention to write a loop instead of i = 2, in order to cycle trough ps_2,ps_3,...
However I think it's not working since the paste() command returns a string instead of the character string with the name: ps_2.
How can I fix this?
Thanks for the time and effort!
Kind regards,
A fellow datafanatic!
The function you need is get(), which gets the value of the object.
ps_t <- ps_t = NULL
sapply(2:3, function(i) append(ps_t, sum(visible %in% get(paste0("ps_", i)))))
Or simply:
sapply(2:3, function(i) sum(visible %in% get(paste0("ps_", i))))
Output
[1] 1 0
You can use eval in R to convert the string to a variable name. You can find the solution here.
Here's what your code will look like:
ps_t <- c(0, (sum(visible %in% eval(parse(text = paste("ps_", i, sep=""))))))
It will give you a numeric vector.
OR
You can use get.
ps_t <- append(0, sum(visible %in% get(paste("ps_", i, sep = ""))))
ps_t

In r, use string just as if I had typed it in

I am dealing with one aspect of r that really confuses me. What I have built is a line of code invoking str_remove saved as a string. If I was to copy-paste that string into where I want to use this line of code, it works perfectly as intended.
However I cannot get r to interpret this code correctly. I have tried using e.g. parse, but the escape characters intended for str_remove regular expression throw up errors.
Is there not a simple way to just treat a string as if it was a line of typed code?
Here is my reproducible example:
Make toy data:
maf_list_context <- list(as.data.frame(cbind(c("ATTATCGAATT", "ATTATTTTAAA"), c("this one", "not that one"))),
as.data.frame(cbind(c("ATTACGTAATT", "ATTATTTTAAA"), c("this one too", "not that one either"))) )
maf_list_context <- lapply(maf_list_context, function(x)
{colnames(x) <- c("CONTEXT", "want_it")
return(x)
})
The idea is that context will be an argument to a function and that it can be flexible, so the user can supply any number of contexts of interest separated by commas. These will be stringr regular expressions designed to look for particular contexts in DNA within a string of 11 bases. Here for example we can use two contexts of interest. The code that follows combines these to make an expression for use later in selecting the appropriate rows from the dataframes in the list.
context <- "\\w{5}CG\\w{4}, \\w{4}CG\\w{5}"
contextvec <- unlist(str_split(context, pattern = ", "))
contextexpression <- c()
for(i in 1:length(contextvec)){
contextexpression <- paste0(contextexpression, "str_detect(x$CONTEXT, pattern = '", contextvec[i], "') |")
}
contextexpression <- str_remove(contextexpression, pattern = " \\|$")
'contextexpression' is now:
[1] "str_detect(x$CONTEXT, pattern = '\\w{5}CG\\w{4}') |str_detect(x$CONTEXT, pattern = '\\w{4}CG\\w{5}')"
If I were to paste this expression directly into apply, it works precisely as I would want it.
> lapply(maf_list_context, function(x){
+
+ x[str_detect(x$CONTEXT, pattern = '\\w{5}CG\\w{4}') |str_detect(x$CONTEXT, pattern = '\\w{4}CG\\w{5}'), ]
+
+ })
[[1]]
CONTEXT want_it
1 ATTATCGAATT this one
[[2]]
CONTEXT want_it
1 ATTACGTAATT this one too
But of course if I use the string there, it does not.
> lapply(maf_list_context, function(x){
+
+ x[contextexpression, ]
+
+ })
[[1]]
CONTEXT want_it
NA <NA> <NA>
[[2]]
CONTEXT want_it
NA <NA> <NA>
I have tried many different functions but none of them make this work. Is there are way of having r interpret this string as if I had typed it in directly?
The whole reprex:
if (!require("stringr") {
install.packages("stringr", dependencies = TRUE)
library("stringr")
maf_list_context <- list(as.data.frame(cbind(c("ATTATCGAATT", "ATTATTTTAAA"), c("this one", "not that one"))),
as.data.frame(cbind(c("ATTACGTAATT", "ATTATTTTAAA"), c("this one too", "not that one either"))) )
maf_list_context <- lapply(maf_list_context, function(x){
colnames(x) <- c("CONTEXT", "want_it")
return(x)
})
context <- "\\w{5}CG\\w{4}, \\w{4}CG\\w{5}"
contextvec <- unlist(str_split(context, pattern = ", "))
contextexpression <- c()
for(i in 1:length(contextvec)){
contextexpression <- paste0(contextexpression, "str_detect(x$CONTEXT, pattern = '", contextvec[i], "') |")
}
contextexpression <- str_remove(contextexpression, pattern = " \\|$")
maf_list_select <- lapply(maf_list_context, function(x){
x[contextexpression, ]
})
I'm not sure I completely follow what you want your input to be and how to apply it, but your problem seems to be with what you're passing to the subset operator, i.e. x[<codehere>]
The subset operator expects a logical vector. When you "paste the expression" you are actually pasting an expression that gets evaluated to a logical vector, hence it properly subsets. When you pass the variable contextexpression, you are actually passing a string. As R sees it:
x[ "str_detect(x$CONTEXT, pattern = '\\w{5}CG\\w{4}') |str_detect(x$CONTEXT, pattern = '\\w{4}CG\\w{5}')", ]
Instead of (notice the syntax highlighting difference):
x[ str_detect(x$CONTEXT, pattern = '\\w{5}CG\\w{4}') |str_detect(x$CONTEXT, pattern = '\\w{4}CG\\w{5}'), ]
You want apply each context to each member of the list to get a logical vector and then subset.
purrr::map2(maf_list_context, contextvec, ~.x[str_detect(.x$CONTEXT, .y), ])
If you want to compare every item in contextvec to every item in maf_list_context, then it's a little more complicated but doable.
purrr::map2(
maf_list_context,
purrr::map(
maf_list_context,
function(data){
purrr::reduce(contextvec,
function(prev, cond) str_detect(data$CONTEXT, cond) | prev,
.init = logical(length(contextvec))
)
}
),
~.x[.y]
)
There's probably a more efficient way to short circuit the matching against the items in maf_list_context, but the general approach applies. The str_detect handles the comparison of a single condition against a single maf_list item. The reduce call combines the results of all the comparisons of contextvec to a single item in maf_list_context to a single boolean. The inner map iterates through maf_list_context. The outer map2 iterates through the list of boolean values created by the inner map and maf_list_context to subset for matches.
If maf_list_context has n items and contextvec has m items:
reduce makes m comparisons, resulting in 1 value
map makes n calls to reduce result in n values
map2 makes n iterations to subset maf_list_context

Modify the object without using return in R function

I am trying to reverse a string without using extra space in R. Below is the code for the same. My question is how to get the ReverseString function change the input without using extra space. I even tried using <<- without any luck.
ReverseString <- function(TestString){
TestString <- unlist(strsplit(TestString, ""))
Left <- 1
Right <- length(TestString)
while (Left < Right){
Temp <- TestString[Left]
TestString[Left] <- TestString[Right]
TestString[Right] <- Temp
Left <- Left + 1
Right <- Right - 1
}
return(paste(TestString, collapse = ""))
}
## Input
a = "StackOverFlow"
## OutPut
ReverseString(a)
"wolFrevOkcatS"
##
a
"StackOverFlow"
It is always better to take advantage of the vectorization in R (instead of for or while loops). So, in base-R, without any packages, it would be something like:
ReverseString <- function(x) {
#splitstring splits every character, and rev reverses the order
out <- rev(strsplit(x, split = '')[[1]])
#paste to paste them together
paste(out, collapse = '')
}
a <- "StackOverFlow"
ReverseString(a)
#[1] "wolFrevOkcatS"
According to your comment you want to reverse the string without calling any function that does the reversal, i.e. no rev and co. Both of the solutions below do this.
I think you are also trying to modify global a from within the function, which is why you tried <<-. I'm not sure why it didn't work for you, but you might have used it incorrectly.
You should know that using <<- alone does not mean that you are using less space. To really save space you would have to call or modify global a at each step in your function where you call or modify TestString. This would entail some combination of assign, do.call, eval and parse - not to mention all the pasteing you would have to do to access elements of a by integer position. Your function would end up bulky, nearly unreadable, and very likley less efficient due to the numerous function calls, despite having saved a negligible amount of space by not storing a copy of a. If you're dead set on creating such an abomination, then take a look at the functions I just listed and figure out how to use them.
Your energy would be better spent by improving upon you string-reversing function in other ways. For example, you can shorten it quite a bit by using a numerical sequence such as 13:1 in sapply:
reverse_string <- function(string) {
vec <- str_split(string, "")[[1]]
paste(sapply(length(vec):1, function(i) vec[i]), collapse = "")
}
reverse_string("StackOverFlow")
#### OUTPUT ####
[1] "wolFrevOkcatS"
If your interviewers also have a problem with reverse sequences then here's another option that's closer to your original code, just a little cleaner. I also did my best to eliminate other areas where "extra space" was being used (indices stored in single vector, no more Temp):
reverse_string2 <- function(string){
vec <- str_split(string, "")[[1]]
i_vec <- c(1, length(vec))
while(i_vec[1] < i_vec[2]) {
vec[i_vec] <- vec[c(i_vec[2], i_vec[1])]
i_vec <- i_vec + c(1, -1)
}
paste(vec, collapse = "")
}
reverse_string2("StackOverFlow")
#### OUTPUT ####
[1] "wolFrevOkcatS"
It can be done easily with stringi
library(stringi)
a <- "StackOverFlow"
stri_reverse(a)
#[1] "wolFrevOkcatS"
I'm not sure I understood exactly the problem, but I think you're looking for a way to reverse the string object and automatically assign it to the original object without having to do a <- ReverseString(a) (assuming this is the reason why you tried using <<-). My solution to this is using deparse(substitute()) to read the original variable name inside the function and assign (using envir = .GlobalEnv) to assign your result over the original variable.
ReverseString <- function(TestString){
nm <- deparse(substitute(TestString))
TestString <- unlist(strsplit(TestString, ""))
Left <- 1
Right <- length(TestString)
while (Left < Right){
Temp <- TestString[Left]
TestString[Left] <- TestString[Right]
TestString[Right] <- Temp
Left <- Left + 1
Right <- Right - 1
}
assign(nm, paste(TestString, collapse = ""), envir = .GlobalEnv)
}
## Input
a = "StackOverFlow"
ReverseString(a)
a
#[1] "wolFrevOkcatS"

Resolving a formatter string

Suppose I have the following:
format.string <- "#AB#-#BC#/#DF#" #wanted to use $ but it is problematic
value.list <- c(AB="a", BC="bcd", DF="def")
I would like to apply the value.list to the format.string so that the named value is substituted. So in this example I should end up wtih a string: a-bcd/def
I tried to do it like the following:
resolved.string <- lapply(names(value.list),
function(x) {
sub(x = save.data.path.pattern,
pattern = paste0(c("#",x,"#"), collapse=""),
replacement = value.list[x]) })
But it doesn't seem to be working correctly. Where am I going wrong?
The glue package is designed for this. You can change the opening and closing delimiters using .open and .close, but they have to be different. Also note that value.list has to be either a list or a dataframe:
library(glue)
format.string <- "{AB}-{BC}/{DF}"
value.list <- list(AB="a", BC="bcd", DF="def")
glue_data(value.list, format.string)
# a-bcd/def
To answer your actual question, by using lapply over names(value.list) you, as your output shows, take each of the elements of value.list and perform the replacement. However, all this happens independently, i.e., the replacements aren't ultimately combined to a single result.
As to make something very similar to your approach work, we can use Reduce which does exactly this combining:
Reduce(function(x, y) sub(paste0(c("#", y, "#"), collapse = ""), value.list[y], x),
init = format.string, names(value.list))
# [1] "a-bcd/def"
If we call the anonymous function f, then the result is
f(f(f(format.string, "A"), "B"), "C")
exactly as you intended, I believe.
We can use gsubfn that can take a key/value pair as replacement to change the pattern with the 'value'
library(gsubfn)
gsub("#", "", gsubfn("[^#]+", as.list(value.list), format.string))
#[1] "a-bcd/def"
NOTE: 'value.list' is a vector and not a list

replacing variable in a string(url) with a list of values in R

I'm trying to generate URLs that follow a standard pattern: abc.xvz/var/
I have a list of values, which I want to insert in the place of 'var'
my code looks something like:
library(gsubfn)
t <- function(l){
u <- "abc.xyz/var/"
gsubfn(pattern = 'var',x = u, replacement = l)
}
test <- do.call(t, list)
however, I get the unused arguments error, and the result makes use of only the first item inside list
What am I doing wrong?
Use lapply:
u <- "abc.xyz/var/"
urls <- lapply(l, function(x) gsub("var", x, u))
We can use sapply with t to get a vector output
sapply(lst, t)
where lst is the list of urls.

Resources