Sine Wave (plotting with digits)

[This article was first published on Numbers around us - Medium, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Excel BI’s Excel Challenge #305 — solved in R

Defining the Puzzle

Today task from ExcelBI which is under this link has another approach. We are not manipulate numbers or string to check their properties, but we are painting with them. That is why I will not upload exercise file today. Here it is:
Print Sine Wave for Amplitudes given in B2
Example for Amplitudes 2, 3 and 4 are given.
Always 5 columns only need to be filled in.

And it looks like this when ready:

So let’s start it in R. Our pictures (sine waves) will be printed in console.

library(tibble)
library(purrr)
library(data.table)

Approach 1: Tidyverse with purrr

generate_wave <- function(amp) {
 n_rows <- 2 * amp — 1
 generate_palindrome <- function(base) {
 sequence <- base:1
 number <- c(sequence, rev(sequence[-length(sequence)]))
 return(paste0(number, collapse = “”))
 }

single_column <- rep(NA, n_rows)
 seq = c(1:amp, (amp-1):1)
 table = tibble(row = seq) %>%
 mutate(row_number = row_number(),
 col1 = map_chr(row, ~generate_palindrome(.x)),
 col2 = col1,
 col3 = col1,
 col4 = col1,
 col5 = col1) %>%
 mutate_at(vars(col1, col3, col5), 
 ~ifelse(row_number > amp, “ “, .)) %>%
 mutate_at(vars(col2, col4), 
 ~ifelse(row_number < amp, “ “, .)) %>%
 mutate_at(vars(col1, col2, col3, col4, col5), 
 ~str_pad(., max(nchar(.), na.rm = TRUE), side = “both”)) %>%
 select(-c(row_number, row))
 return(table)
}

x <- generate_wave(6)
print(x, n = Inf)

Approach 2: Base R

generate_wave_base <- function(amp) {
 n_rows <- 2 * amp — 1
 
 generate_palindrome <- function(base) {
 sequence <- base:1
 number <- c(sequence, rev(sequence[-length(sequence)]))
 return(paste0(number, collapse = “”))
 }
 
 seq <- c(1:amp, (amp-1):1)
 table <- data.frame(row = seq)
 table$row_number <- seq_along(table$row)
 
 table$col1 <- sapply(table$row, generate_palindrome)
 table$col2 <- table$col1
 table$col3 <- table$col1
 table$col4 <- table$col1
 table$col5 <- table$col1
 
 condition_space <- table$row_number > amp
 condition_empty <- table$row_number < amp
 
 table$col1[condition_space] <- “ “
 table$col3[condition_space] <- “ “
 table$col5[condition_space] <- “ “
 
 table$col2[condition_empty] <- “ “
 table$col4[condition_empty] <- “ “
 
 max_length <- max(nchar(c(table$col1, table$col2, table$col3, table$col4, table$col5)), na.rm = TRUE)
 
 pad_string <- function(string, max_length) {
 current_length <- nchar(string)
 side_length <- (max_length — current_length) %/% 2
 return(paste0(paste(rep(“ “, side_length), collapse = “”), string))
 }
 
 table$col1 <- sapply(table$col1, pad_string, max_length = max_length)
 table$col2 <- sapply(table$col2, pad_string, max_length = max_length)
 table$col3 <- sapply(table$col3, pad_string, max_length = max_length)
 table$col4 <- sapply(table$col4, pad_string, max_length = max_length)
 table$col5 <- sapply(table$col5, pad_string, max_length = max_length)
 
 table$row_number <- NULL
 table$row <- NULL
 
 table = as_tibble(table)
 
 return(table)
}

x_base = generate_wave_base(6)
print(x_base)

Approach 3: Data.table

generate_wave_dt <- function(amp) {
 n_rows <- 2 * amp — 1
 
 generate_palindrome <- function(base) {
 sequence <- base:1
 number <- c(sequence, rev(sequence[-length(sequence)]))
 return(paste0(number, collapse = “”))
 }
 
 seq <- c(1:amp, (amp-1):1)
 table <- data.table(row = seq)
 
 table[, row_number := .I] 
 table[, col1 := sapply(row, generate_palindrome)]
 
 table[, `:=` (col2 = col1, col3 = col1, col4 = col1, col5 = col1)]
 
 table[row_number > amp, `:=` (col1 = “ “, col3 = “ “, col5 = “ “)]
 table[row_number < amp, `:=` (col2 = “ “, col4 = “ “)]

max_length <- max(nchar(c(table$col1, table$col2, table$col3, table$col4, table$col5)), na.rm = TRUE

pad_string <- function(strings, max_length) {
 padded_strings <- vapply(strings, function(string) {
 current_length <- nchar(string)
 
 total_padding <- max_length — current_length
 
 if (total_padding <= 0) return(string)
 
 side_padding <- floor(total_padding / 2)

padded_string <- paste0(
 strrep(“ “, side_padding), 
 string, 
 strrep(“ “, total_padding — side_padding) 
 )
 return(padded_string)
 }, FUN.VALUE = character(1)) 
 return(padded_strings)
 }
 
 table[, (c(“col1”, “col2”, “col3”, “col4”, “col5”)) := lapply(.SD, function(x) pad_string(x, max_length)), .SDcols = c(“col1”, “col2”, “col3”, “col4”, “col5”)]
 table[, c(“row_number”, “row”) := NULL]
 
 return(table)
}

x_dt = generate_wave_dt(6)
print(x_dt)

Validation

As we don’t have to validate it another way than looking at them and confirm that it looks like sine wave, I can say, it works well. Let check how it will look with not 6 but 9 digits.

However I make little bechmark as well to check which is best in time consumption:

library(microbenchmark)
microbenchmark(generate_wave(6), generate_wave_base(6), generate_wave_dt(6), times
 = 100)

And what we see? Easiest to do are ussually not fast enough. Fastest one is base R approach, but it has very important cause: base approach is basing on simple, but computationally powerful data structure: matrix.

Do not hesitate to show your approaches in R or even Python. Comment, share and come back for next puzzles.


Sine Wave (plotting with digits) was originally published in Numbers around us on Medium, where people are continuing the conversation by highlighting and responding to this story.

To leave a comment for the author, please follow the link and comment on their blog: Numbers around us - Medium.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)