IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    R Solution for Excel Puzzles

    Numbers around us发表于 2024-02-12 13:22:50
    love 0
    [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.

    Puzzles no. 379–388

    Puzzles

    Author: ExcelBI

    All files (xlsx with puzzle and R with solution) for each and every puzzle are available on my Github. Enjoy.

    Last week I were on my winter holiday and with refreshed mind, it is time to get back to work, to everyday routines, to our puzzles and challenges. I owe you my solutions for challenges for two weeks now. So If you are asking me, if I’m back, look below …

    https://medium.com/media/a949e942ee04750f2a502445a5b739bc/href

    Puzzle #379

    In this puzzle we have string with sequence of numbers comma separated. Just a series of numbers but our host doesn’t really like them, because they are not really tidy. To make more order in them we have to get only those parts of string that present only numbers followed by larger one. We are checking if for each number next number is greater, and if not drop the number. Let’s code it.

    Loading libraries and data

    library(tidyverse)
    library(readxl)
    
    input = read_excel("Excel/379 All Elements Larger than Preceding one.xlsx", range = "A1:A10")
    test  = read_excel("Excel/379 All Elements Larger than Preceding one.xlsx", range = "B1:B10")

    Transformation

    check_succeeding <- function(numbers, index) {
      current <- numbers[index]
      succeeding <- numbers[(index + 1):length(numbers)]
      all(succeeding > current)
    }
    
    process_string <- function(string) {
      numbers <- str_split(string, ",\\s*")[[1]] %>% 
        as.numeric()
    
      result <- map_lgl(seq_along(numbers), ~check_succeeding(numbers, .)) %>%
        which() %>%
        map_chr(~ as.character(numbers[.])) %>%
        paste(collapse = ", ")
      
      result = ifelse(result == "", NA_character_, result)
      
      return(result)
    }
    
    result = input %>%
      mutate(`Answer Expected` = map_chr(Numbers, ~process_string(.x)))

    Validation

    identical(result$`Answer Expected`, test$`Answer Expected`)
    #> [1] TRUE

    Puzzle #380

    In this puzzle we need to create matrix made from “x’s” instead of numbers, but only sides and diagonals should be filled with “x’s” and the rest should be empty. And all we have to use for this generations have to be side length. So let’s do it.

    Load libraries and data

    library(tidyverse)
    library(readxl)
    
    test1 = read_excel("Excel/380 Draw NxN Squares.xlsx", range = "A2:H9",   col_names = FALSE) %>%
      as.matrix() %>% {attr(., "dimnames") <- NULL; .}
    test2 = read_excel("Excel/380 Draw NxN Squares.xlsx", range = "A11:G17", col_names = FALSE) %>%
      as.matrix() %>% {attr(., "dimnames") <- NULL; .}
    test3 = read_excel("Excel/380 Draw NxN Squares.xlsx", range = "A19:E23", col_names = FALSE) %>%
      as.matrix() %>% {attr(., "dimnames") <- NULL; .}
    test4 = read_excel("Excel/380 Draw NxN Squares.xlsx", range = "A25:D28", col_names = FALSE) %>%
      as.matrix() %>% {attr(., "dimnames") <- NULL; .}

    Transformation

    draw_sides_and_diag = function(matrix_size) {
      mat = matrix(NA, nrow = matrix_size, ncol = matrix_size)
      mat[1,] = "x"
      mat[matrix_size,] = "x"
      mat[,1] = "x"
      mat[,matrix_size] = "x"
      diag(mat) = "x"
      diag(mat[,ncol(mat):1]) = "x"
    
      return(mat)
    }
    
    draw_sides_and_diag(8)
    
         [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
    [1,] "x"  "x"  "x"  "x"  "x"  "x"  "x"  "x" 
    [2,] "x"  "x"  NA   NA   NA   NA   "x"  "x" 
    [3,] "x"  NA   "x"  NA   NA   "x"  NA   "x" 
    [4,] "x"  NA   NA   "x"  "x"  NA   NA   "x" 
    [5,] "x"  NA   NA   "x"  "x"  NA   NA   "x" 
    [6,] "x"  NA   "x"  NA   NA   "x"  NA   "x" 
    [7,] "x"  "x"  NA   NA   NA   NA   "x"  "x" 
    [8,] "x"  "x"  "x"  "x"  "x"  "x"  "x"  "x" 

    Validation

    all.equal(draw_sides_and_diag(8), test1)
    #> [1] TRUE
    all.equal(draw_sides_and_diag(7), test2)
    #> [1] TRUE
    all.equal(draw_sides_and_diag(5), test3)
    #> [1] TRUE
    all.equal(draw_sides_and_diag(4), test4)
    #> [1] TRUE

    Puzzle #381

    In IPv6 addressing system there are some nice tricks that allows to write it shorter in some cases. For example four zeroes can be written as one etc. Let cite those rules from the task:

    IPv6 address is represented as x:x:x:x:x:x:x:x (total 8 x) where x consists of 1 to 4 Hexadecimal digits. Following rules to be followed to shorted an IPv6 address -
    1. Leading 0s should be omitted. Hence, 00A6 should be written as A6.
    2. Double colons (::) should be used in place of a series of contiguous zeros. For example, IPv6 address CD34:0000:0000:0000:0000:0000:0000:A4 can be written as CD34::A4.
    3. Double colons should be used only once in an IP address. Since, we are looking at shortest possible IPv6, hence double colon in this case should be used where more number of series of contiguous 0s are there.
    CD34:0000:0000:2AB6:0000:0000:0000:A4 can be written as CD34:0:0:2AB6::A4 not as CD34::2AB6:0:0:0:A4.

    And what we have to do? Of course shorten given addresses in all possible way.

    Load libraries and data

    library(tidyverse)
    library(readxl)
    
    input = read_excel("Excel/381 IPv6 Shortening.xlsx", range = "A1:A11")
    test  = read_excel("Excel/381 IPv6 Shortening.xlsx", range = "B1:B11")

    Transformation

    shorten_ipv6 = function(ipv6) {
      blocks = str_split(ipv6, ":")[[1]] %>%
      str_replace( "^0+", "") %>%
      str_replace("^$", "0") 
    
      zeros = blocks %>% 
        str_detect("^0+$") %>%
        which() %>%
        unlist()  %>%
        data.frame(x = .) %>%
        mutate(group = cumsum(x - lag(x, default = 0) > 1)) %>%
        group_by(group) %>%
        mutate(n = n(),
               min_index = min(x),
               max_index = max(x)) %>%
        ungroup() %>%
        arrange(desc(n), group) %>%
        slice(1)
    
      first_zero = zeros$min_index
      last_zero = zeros$max_index
      
      block_df = data.frame(blocks = blocks,
                            index = 1:length(blocks),
                            stringsAsFactors = FALSE)
      
      block_df$blocks[block_df$index >= first_zero & block_df$index <= last_zero] = ""
      
      result = block_df$blocks %>%
        str_c(collapse = ":")  %>%
        str_replace(., ":{2,}", "::")
      
      return(result)
    }
    
    result = input %>% 
      mutate(short = map_chr(`IPv6 Addresses`, shorten_ipv6))

    Validation

    identical(result$short, test$`Expected Answer`)
    #> [1] TRUE

    Puzzle #382

    This is one of two puzzles concerning “Kamasutra Cipher” in today’s episode. This kind of cipher is based on randomly mixing set of characters used in cipher, dividing into two equally long groups and then stack it like sandwich. Sandwich -> Kamasutra, your imagination will know what to do with it. 🙂 First puzzle are coding only English alphabet letters. We are not validating anything, because every shuffling is unique.

    Load libraries and data

    library(tidyverse)
    library(readxl)
    
    input = read_excel("Excel/382 Kamasutra Cipher.xlsx", range = "A1:A10")

    Transformation

    generate_code = function() {
    shuffled_alph = sample(letters)
    sh_p1 = shuffled_alph[1:(length(shuffled_alph)/2)] 
    sh_p2 = shuffled_alph[(length(shuffled_alph)/2 + 1):length(shuffled_alph)]
    sh_p1 = setNames(sh_p1, sh_p2)
    sh_p2 = setNames(sh_p2, sh_p1)
    code = c(sh_p1, sh_p2)
    return(code)
    }
    
    code = function(string){
      code = generate_code()
      string = tolower(string)
      words = str_split(string, " ")[[1]]
      chars = map(words, str_split, "") %>%
        map(unlist)
      coded_chars = map(chars, function(x) code[x])
      coded_words = map(coded_chars, paste, collapse = "") 
      coded_string = paste(coded_words, collapse = " ")
      return(coded_string)
    }
    
    result = input %>%
      mutate(coded = map_chr(`Plain Text`, code))

    Puzzle #383

    In this puzzle we need to extract numbers from given string and divide them into positive and negative numbers. Looks like piece of cake. Let’s check it.

    Load libraries and data

    library(tidyverse)
    library(readxl)
    
    input = read_excel("Excel/383 Extract Positive and Negative Numbers.xlsx", range = "A2:A10")
    test  = read_excel("Excel/383 Extract Positive and Negative Numbers.xlsx", range = "B2:C10")

    Transformation

    extract = function(input, sign) {
      numbers = input %>%
        str_extract_all(paste0(sign, "(\\d+)")) %>%
        unlist() %>%
        as.numeric() %>%
        abs() %>%
        unique() %>%
        str_c(collapse = ", ") 
      
      if (numbers == "") {
        numbers = NA_character_
      } else {
        numbers = numbers
      }
    }
    
    result = input %>%
      mutate(positive = map_chr(Strings, extract, "\\+"),
             negative = map_chr(Strings, extract, "\\-")) 

    Validation

    identical(result$positive, test$`Positive Numbers`)
    # [1] TRUE
    
    identical(result$negative, test$`Negative Numbers`)
    # [1] TRUE

    Puzzle #384

    This puzzle is looking for increasing numbers as well, but different way. We have long numbers and what we need to do is:
    - get first digit and,
    - check if second digit is greater than first
    - if not check if two following are greater (and do it unless we find one)
    - if yes slice off first digit and get the number we found above.

    It is rather difficult to explain it in simple words. But it is much more difficult to code it. We are using recursive function here.

    Load data and libraries

    library(tidyverse)
    library(readxl)
    
    input = read_excel("Excel/384 Extract Increasing Numbers.xlsx", range = "A1:A12")
    test  = read_excel("Excel/384 Extract Increasing Numbers.xlsx", range = "B1:B12")

    Transformation

    recursive_append <- function(n, p = 0, c = 1, a = 0) {
      if (p + c > nchar(n)) {
        return("")
      } else {
        v <- as.numeric(substr(n, p + 1, p + c))
        if (!is.na(v) && v > a) {
          b <- paste(", ", v, recursive_append(n, p + c, 1, v), sep = "")
        } else {
          b <- recursive_append(n, p, c + 1, a)
        }
        return(b)
      }
    }
    
    result = input %>%
      rowwise() %>%
      mutate(R = substring(recursive_append(as.character(Numbers)), 1)) %>%
      mutate(R = str_sub(R, 3, -1))

    Validation

    identical(result$R, test$`Answer Expected`)
    # [1] TRUE

    Puzzle #385

    Matrices again. And job can be divided into two parts: populate first matrix (harder one) and create larger matrix by transforming and binding matrices (much easier one).
    First matrix has size 5x5 in which we have first row from 0 to 4 and in others this row is shifted once at the time. Then we need to make matrix with elements bigger by 5 in each cell, and then combine it to matrix given in puzzle.

    Load libraries and data

    library(tidyverse)
    library(readxl)
    
    test = read_excel("Excel/385 Generate the Grid.xlsx", range = "C3:L12", col_names = FALSE) %>%
      as.matrix() %>%
      {attr(., "dimnames") <- NULL; .}

    Transformation

    generate = function(n){
      grid_df <- expand.grid(i = 1:n, j = 1:n) %>% 
        mutate(value = (i + j - 2) %% n) %>%
        pull(value)
      
      matrix(grid_df, nrow = n, ncol = n)
    }
    
    a = generate(5)
    
    b = a + 5
    
    c = cbind(a,b)
    d = cbind(b,a)
    
    result = rbind(c,d) %>% {attr(., "dimnames") <- NULL; .}

    Validation

    identical(result, test)
    # [1] TRUE

    Puzzle #386

    Today we have to extract all numbers that are comfortly hugged by parentheses from both sides. Numbers that has parenthesis only from one side are not important this time. Let’s find them.

    Load libraries and data

    library(tidyverse)
    library(readxl)
    
    input = read_excel("Excel/386 Extract Numbers in Parentheses.xlsx", range = "A1:A10")
    test  = read_excel("Excel/386 Extract Numbers in Parentheses.xlsx", range = "B1:B10")

    Transformation

    extract = function(x) {
      x = str_extract_all(x, "\\((\\d+)\\)") %>%
        unlist() %>%
        str_remove_all("\\D") %>%
        str_c(collapse = ", ")
      if (x == "") x = NA_character_
      return(x)
    }
    
    result = input %>%
      rowwise() %>%
      mutate(result = map_chr(String, extract))

    Validation

    identical(result$result, test$`Answer Expected`)
    # [1] TRUE

    Puzzle #387

    Now we have to construct some weird kind of calendar. Basing on given date we have to make month chart that has weekdays in normal order but weeks upside down so first days of month are at the bottom of months and last ones at the top.
    Weird but not impossible. Let’s do it.

    Load libraries and data

    library(tidyverse)
    library(readxl)
    
    test = read_excel("Excel/387 Fill in the Last Dates.xlsx", range = "B1:H6") %>%
      mutate(across(everything(), as.Date))
    
    date = read_excel("Excel/387 Fill in the Last Dates.xlsx", range = "A1", col_names = FALSE) %>%
      pull()

    Transformation

    df = data.frame(date = seq(floor_date(date, "month"), 
                               ceiling_date(date, "month") - days(1), 
                               by = "day") %>%
                      as.Date()) %>%
      mutate(week = week(date), 
             wday = wday(date, label = T, abbr = T,  week_start = 1, locale = "US_us")) %>%
      pivot_wider(names_from = wday, values_from = date) %>%
      select(week, Mon, Tue, Wed, Thu, Fri, Sat, Sun) %>%
      arrange(desc(week)) %>%
      select(-week)

    Validation

    identical(df, test)
    # [1] TRUE

    Puzzle #388

    And as I said before second puzzle about Kamasutra Cipher comes here. This time not only letters, but also digits come to game.

    Load libraries and data

    library(tidyverse)
    library(readxl)
    
    input = read_excel("Excel/388 Kamasutra Cipher_2.xlsx", range = "A1:A10")

    Transformation

    generate_code = function() {
      shuffled_alph = sample(letters)
      sh_p1 = shuffled_alph[1:(length(shuffled_alph)/2)] 
      sh_p2 = shuffled_alph[(length(shuffled_alph)/2 + 1):length(shuffled_alph)]
      sh_p1 = setNames(sh_p1, sh_p2)
      sh_p2 = setNames(sh_p2, sh_p1)
      code = c(sh_p1, sh_p2)
      
      shuffled_digits = sample(0:9)
      sh_d1 = shuffled_digits[1:(length(shuffled_digits)/2)]
      sh_d2 = shuffled_digits[(length(shuffled_digits)/2 + 1):length(shuffled_digits)]
      sh_d1 = setNames(sh_d1, sh_d2)
      sh_d2 = setNames(sh_d2, sh_d1)
      code = c(code, sh_d1, sh_d2)
      return(code)
    }
    
    code = function(string){
      code = generate_code()
      string = tolower(string)
      words = str_split(string, " ")[[1]]
      chars = map(words, str_split, "") %>%
        map(unlist)
      coded_chars = map(chars, function(x) code[x])
      coded_words = map(coded_chars, paste, collapse = "") 
      coded_string = paste(coded_words, collapse = " ")
      return(coded_string)
    }
    
    result = input %>%
      mutate(coded = map_chr(`Plain Text`, code))

    Feel free to comment, share and contact me with advices, questions and your ideas how to improve anything. Contact me on Linkedin if you wish as well.


    R Solution for Excel Puzzles 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.
    Continue reading: R Solution for Excel Puzzles


沪ICP备19023445号-2号
友情链接