Recording output from loops in R - r

I am doing an exercise in my R class, and I hope you can help. The task is to create my own script that determines whether or not a number is a palindrome. My idea was to create a repetition structure that records each digit in a number of any size, compares those digits in order, and then makes a call as to whether the number is a palindrome or not.
So far, I thought I could use the "for" command to break the number down, like this:
# Initialize
Number <- 242
Number
N <- nchar(Number)
N
# Find numbers and digits
if (Number == 0) {
print ("Number must be greater than 0")
}
if (Number < 0) {
print ("Number must be greater than 0")
}
for (i in 1:N) {
print (Number)
Digit <- Number %/% 10^(N-1)
print (Digit)
Number <- Number %% 10^(N-1)
N <- N-1
}
The problem, though, is that since this structure overwrites the variables in each loop, I cannot print all the digits out separately once the loop is done. Can I command R to print out and record the digits produced in each loop, so that they can be compared to each other downstream and used to assess whether the original number was a palindrome or not? Thanks for your help.

There's better ways of checking for palindrome-ness in R, for which you should see the other answers. For your specific problem of keeping track of things during a for loop, one approach is to make a vector that's as long as the for loop and assign to the ith element of the vector in the ith iteration of the loop.
Number <- 12345
N <- nchar(Number)
backwardsDigits <- numeric(N) ## a vector of numerics of length N
for (i in N:1) {
backwardsDigits[i] <- Number %/% 10^(i-1)
Number <- Number %% 10^(i-1)
}
backwardsDigits
all(backwardsDigits == rev(backwardsDigits))
You could use forwardsDigits instead by writing to forwardsDigits[N - i + 1] in the loop. You don't really need to print anything during the loop, though it can be helpful for debugging.

As #thelatemail suggested, there is another (perhaps more intuitive way) to do this.
First, let's convert the number 117711 to a string and split it up.
charsplit <- strsplit(as.character(117712), "")
[[1]]
[1] "1" "1" "7" "7" "1" "2"
Then, we'll take it out of list form and reverse it
revchar <- rev(unlist(charsplit))
[1] "2" "1" "7" "7" "1" "1"
Finally, we'll paste these together and convert them into a number:
palinum <- as.numeric(paste(revchar, collapse=""))
[1] "217711"
We can then check if they're identical:
117712 == palinum
[1] FALSE
We can even write a function to do it for us.
is.palindrome <- function(number){
charsplit <- strsplit(as.character(number), "")
revchar <- rev(unlist(charsplit))
palinum <- as.numeric(paste(revchar, collapse=""))
number==palinum
}
is.palindrome(117712)
[1] FALSE
is.palindrome(117711)
[1] TRUE

Related

strsplit(rquote, split = "")[[1]] in R

rquote <- "r's internals are irrefutably intriguing"
chars <- strsplit(rquote, split = "")[[1]]
This question has been asked before on this forum and has one answer on it but I couldn't understand anything from that answer, so here I am asking this question again.
In the above code what is the meaning of [[1]] ?
The program that I'm trying to run:
rquote <- "r's internals are irrefutably intriguing"
chars <- strsplit(rquote, split = "")[[1]]
rcount <- 0
for (char in chars) {
if (char == "r") {
rcount <- rcount + 1
}
if (char == "u") {
break
}
}
print(rcount)
When I don't use [[1]] I get the following warning message in for loop and I get a wrong output of 1 for rcount instead of 5:
Warning message: the condition has length > 1 and only the first element will be used
strsplit is vectorized. That means it splits each element of a vector into a vectors. To handle this vector of vectors it returns a list in which a slot (indexed by [[) corresponds to a element of the input vector.
If you use the function on a one element vector (single string as you do), you get a one-slot list. Using [[1]] right after strsplit() selects the first slot of the list - the anticipated vector.
Unfortunately, your list chars works in a for loop - you have one iteration with the one slot. In if you compare the vector of letters against "r" which throws the warning. Since the first element of the comparison is TRUE, the condition holds and rcount is rised by 1 = your result. Since you are not indexing the letters but the one phrase, the cycle stops there.
Maybe if you run something like strsplit(c("one", "two"), split="") , the outcome will be more straightforward.
> strsplit(c("one", "two"), split="")
[[1]]
[1] "o" "n" "e"
[[2]]
[1] "t" "w" "o"
> strsplit(c("one", "two"), split="")[[1]]
[1] "o" "n" "e"
> strsplit(c("one"), split="")[[1]][2]
[1] "n"
We'll start with the below as data, without [[1]]:
rquote <- "r's internals are irrefutably intriguing"
chars2 <- strsplit(rquote, split = "")
class(chars2)
[1] "list"
It is always good to have an estimate of your return value, your above '5'. We have both length and lengths.
length(chars2)
[1] 1 # our list
lengths(chars2)
[1] 40 # elements within our list
We'll use lengths in our for loop for counter, and, as you did, establish a receiver vector outside the loop,
rcount2 <- 0
for (i in 1:lengths(chars2)) {
if (chars2[[1]][i] == 'r') {
rcount2 <- rcount2 +1
}
if (chars2[[1]][i] == 'u') {
break
}
}
print(rcount2)
[1] 6
length(which(chars2[[1]] == 'r')) # as a check, and another way to estimate
[1] 6
Now supposing, rather than list, we have a character vector:
chars1 <- strsplit(rquote, split = '')[[1]]
length(chars1)
[1] 40
rcount1 <- 0
for(i in 1:length(chars1)) {
if(chars1[i] == 'r') {
rcount1 <- rcount1 +1
}
if (chars1[i] == 'u') {
break
}
}
print(rcount1)
[1] 5
length(which(chars1 == 'r'))
[1] 6
Hey, there's your '5'. What's going on here? Head scratch...
all.equal(chars1, unlist(chars2))
[1] TRUE
That break should just give us 5 'r' before a 'u' is encountered. What's happening when it's a list (or does that matter...?), how does the final r make it into rcount2?
And this is where the fun begins. Jeez. break for coffee and thinking. Runs okay. Usual morning hallucination. They come and go. But, as a final note, when you really want to torture yourself, put browser() inside your for loop and step thru.
Browse[1]> i
[1] 24
Browse[1]> n
debug at #7: break
Browse[1]> chars2[[1]][i] == 'u'
[1] TRUE
Browse[1]> n
> rcount2
[1] 5

making for loop for character vector in R

char_vector <- c("Africa", "identical", "ending" ,"aa" ,"bb", "rain" ,"Friday" ,"transport") # character vector
Suppose I have the above character vector
I would like to create a for loop to print on the screen only the elements in a vector that have more than 5 characters and starts with a vowel
and also delete from the vector those elements that do not start with a vowel
I created this for loop but it also gives null characters
for (i in char_vector){
if (str_length(i) > 5){
i <- str_subset(i, "^[AEIOUaeiou]")
print(i)
}
}
The result for the above is
[1] "Africa"
[1] "identical"
[1] "ending"
character(0)
character(0)
My desired result would only be the first 3 characters
I'm really new to R and facing huge difficulty with creating a for loop for this problem. Any help would be greatly appreciated!
Use grepl with the pattern ^[AEIOUaeiuo]\w{5,}$:
char_vector <- c("Africa", "identical", "ending" ,"aa" ,"bb", "rain" ,"Friday" ,"transport")
char_vector <- char_vector[grepl("^[AEIOUaeiuo]\\w{5,}$", char_vector)]
char_vector
[1] "Africa" "identical" "ending"
The regex pattern used here says to match words which:
^ from the start of the word
[AEIOUaeiuo] starts with a vowel
\w{5,} followed by 5 or more characters (total length > 5)
$ end of the word
You don't need for loop, because we use vectorized functions in R.
A simple solution using grep and substr (refer to Tim Blegeleisen answer for details):
substr(grep('^[aeiu].{4}', char_vector, T, , T), 1, 3)
# [1] "Afr" "ide" "end"
With stringr functions, you'd rather use str_detect instead of str_subset, and you can take advantage of the fact that those functions are vectorized:
library(stringr)
char_vector[str_length(char_vector) > 5 & str_detect(char_vector, "^[AEIOUaeiou]")]
#[1] "Africa" "identical" "ending"
or if you want your for loop as a single vector:
vec <- c()
for (i in char_vector){
if (str_length(i) > 5 & str_detect(i, "^[AEIOUaeiou]")){
vec <- c(vec, i)
}
}
vec
# [1] "Africa" "identical" "ending"
The first 3 characters?
library(stringr)
for (i in char_vector){
if (str_length(i) > 5 & str_detect(i, "^[AEIOUaeiou]")) {
word <- str_sub(i, 1, 3)
print(word)
}
}
output is:
[1] "Afr"
[1] "ide"
[1] "end"
Using only base R functions. No need for a loop. I wrapped the steps in a function so you can use the function with other character vectors. You could make this code shorter (see #utubun's answer) but I feel it is easier to understand the process with a "one line one step" approach.
char_vector <- c("Africa", "identical", "ending" ,"aa" ,"bb", "rain" ,"Friday" ,"transport")
yourfun <- function(char_vector){
char_vector <- char_vector[nchar(char_vector)>= 5] # grab only the strings that are at least 5 characters long
char_vector <- char_vector[grep(pattern = "^[AEIOUaeiou]", char_vector)] # grab strings that starts with vowel
return(char_vector) # print the first three strings
# remove comments to get the first three characters of each string
# out <- substring(char_vector, 1, 3) # select only the first 3 characters of each string
# return(out)
}
yourfun(char_vector = char_vector)
#> [1] "Africa" "identical" "ending"
Created on 2022-05-09 by the reprex package (v2.0.1)

Setting maximum numbers of characters in number

I want to have number with respect to maximum number of characters. e.g. let's take value 517.1918
I want to set that maximum number of characters to three, then it should give mu just 517 (just three first characters)
My work so far
I tried so split my number into to parts : first one containing three first numbers and second one containing remaining numbers by a code following :
d_convert<-function(x){
x<-sub('(.{3})(.{2})', '\\1', x)
x
}
d_convert(12345)
And it work's, but I'm not sure how can I put instead of (.{2}), length(x)-3. I tried print(paste()) but it didn't work. Is there any simply way how to do it ?
Try using signif which rounds a number to a given number of significant digits.
> signif(517.1918, 3)
[1] 517
I'm not sure if I understood what want, but you can try this:
d_convert2 <-function(x, digits=3){
x <- gsub("\\D", "", x)
num_string <- strsplit(x, "")[[1]]
out <- list(digits = num_string[1L:digits], renaming = num_string[(digits+1):length(num_string)])
out <- lapply(out, paste0, collapse="")
return(out)
}
> d_convert2(12345)
$digits
[1] "123"
$renaming
[1] "45"
> d_convert2("1,234.5")
$digits
[1] "1" "2" "3"
$renaming
[1] "4" "5"

put left padded zeros inside string

i want to write a function which takes a character Vector(including numbers) as Input and left pads zeroes to the numbers in it. for example this could be an Input Vector :
x<- c("abc124.kk", "77kk-tt", "r5mm")
x
[1] "abc124.kk" "77kk-tt" "r5mm"
each string of the input Vector contains only one Vector but there all in different positions(some are at the end, some in the middle..)
i want the ouput to look like this:
"abc124.kk" "077kk-tt" "r005mm"
that means to put as many leading Zeros to the number included in the string so that it has as many Digits as the longest number.
but i want a function who does this for every string Input not only my example(the x Vector).
i already started extracting the numbers and letters and turned the numbers the way i want them but how can i put them back together and back on the right Position?
my_function<- function(x){
letters<- str_extract_all(x,"[a-z]+")
numbers<- str_extract_all(x, "[0-9]+")
digit_width<-max(nchar(numbers))
numbers_correct<- str_pad(numbers, width=digit_width, pad="0")
}
and what if i have a Vector which includes some strings without numbers? how can i exclude them and get them back without any changes ?
for example if teh Input would be
y<- c("12ab", "cd", "ef345")
the numbers variable Looks like that:
[[1]]
[1] "12"
[[2]]
character(0)
in this case i would want that the ouput at the would look like this:
"012ab" "cd" "ef345"
An option would be using gsubfn to capture the digits, convert it to numeric and then pass it to sprintf for formatting
library(gsubfn)
gsubfn("([0-9]+)", ~ sprintf("%03d", as.numeric(x)), x)
#[1] "abc124.kk" "077kk-tt" "r005mm"
x <- c("12ab", "cd", "ef345")
s = gsub("\\D", "", x)
n = nchar(s)
max_n = max(n)
sapply(seq_along(x), function(i){
if (n[i] < max_n) {
zeroes = paste(rep(0, max_n - n[i]), collapse = "")
gsub("\\d+", paste0(zeroes, s[i]), x[i])
} else {
x[i]
}
})
#[1] "012ab" "cd" "ef345"

R: replace the whole part of a number but keep the decimal part unchanged

ok I'm very new to R and this is a general question. I have various numbers and I want to replace the whole part and keep the decimal part unchanged.
For example I have values from 25.01 to 25.99, I want to replace 25 with 10 but keep the decimal part unchanged (so my new numbers would be from 10.01 to 10.99).
Is that possible?
The commenters are right. It doesn't matter how long the list of numbers is. R can do the operation in one go.
x <- c(25.01, 25.8, 25.4)
x-15
[1] 10.01 10.80 10.40
If you do run into a situation with many wide-ranging numbers and need to literally keep what comes after the decimal and pick a number to put in front, use something like this:
keep.decimal <- function(x, n=10) {
paste0(n, gsub('\\d*(\\.\\d*)', '\\1', x))
}
Then you can even choose which number you want in front of it.
keep.decimal(x)
[1] "10.01" "10.8" "10.4"
keep.decimal(x, 50)
[1] "50.01" "50.8" "50.4"
Explanation of vectorization
When a vector is defined like x <- 1:5, R will attempt to execute operations element by element when possible. If 2 had to be added to each element of x and assigned to a new variable y, it might be difficult with a for loop:
y <- NULL
for (i in 1:length(x)) {
y[i] <- x[i] + 2
}
y
[1] 3 4 5 6 7
But with R, this operation is simplified to:
y <- x + 2
This operational technique extends further than arithmetic. Here's a problem to solve, write an expression that matches a lowercase letter to its capitalized counterpart. So turn this:
x <- c("a", "b", "c")
x
[1] "a" "b" "d"
Into
y
[1] "aA" "bB" "dD"
I can write
y <- paste0(x, LETTERS[match(x, letters)])
y
[1] "aA" "bB" "dD"
Instead of:
y <- NULL
for(i in 1:length(x)) {
y[i] <- paste0(x[i], LETTERS[match(x[i], letters)])
}
y
[1] "aA" "bB" "dD"
It is much faster too. As an analogy, think of restaurant service. R is your server ready to fetch what you want. If you ask your server for ketchup, they get the ketchup and deliver it. Then you ask for a napkin. They go back to get the napkin and deliver it. Then you ask for a refill.
This process takes a long time. If you would have asked for ketchup, a napkin, and a refill at the same time, the server would be able to optimize their trip to get what you want fastest. R has optimized functions built in the 'C' language that are lightning fast, and we exploit that speed every time we vectorize.

Resources