Sine Wave (plotting with digits)
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.
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.