Categories

# Advent of Code 2017 in #rstats: Day 2

After reading this puzzle, I was excited and concerned to see “spreadsheet”:

1. Excited: R is good at rectangular numeric data, and I work with a lot of spreadsheets
2. Concerned: Will I be starting with a single long string?  How do I get that into rectangular form?

After considering trying to chop up one long string, I copy-pasted my input to a text file and `read.delim()` worked on the first try.

Part 1 was simple, Part 2 was tricky.  The idea of row-wise iteration got in my head and I spent a lot of time with `apply` and `lapply`… I still hold a grudge against them from battles as a new R user, but having (mostly) bent them to my will I went to them here.  Maybe I should spend more time with the `purrr` package.

### Part 1

Straightforward:

```library(dplyr)
library(testthat)

ex <- data.frame(
a = c(5, 7, 2),
b = c(1,5,4),
c = c(9,3,6),
d = c(5, NA, 8)
)

checksum <- function(x){
row_max <- apply(x, 1, max, na.rm = TRUE)
row_min <- apply(x, 1, min, na.rm = TRUE)
sum(row_max - row_min)
}

expect_equal(checksum(ex), 18) # works!
checksum(my_input)
```

### Part 2

Got the idea quickly, but executing took a bit longer.  I decided right away that I would:

1. Work on a function that tackled a row (as a vector) at a time
2. The function would divide every value in the vector by every other value in the vector
3. It would then pick out the integer that wasn’t 1, as well as its reciprocal, and divide them
4. Then run this function row-wise on the input

Actually building up that function took ~20 minutes.  I felt confident the approach would work so didn’t stop to consider anything else… I’m excited to see other solutions after I publish this, as maybe there’s something elegant I missed.

Along the way I learned: `is.integer` doesn’t do what I thought it would.  Come on, base R.

I don’t work with matrices much (not a mathematician, don’t have big data) and was a somewhat vexed by the matrix -> data.frame conversion.

```# Will apply something row-wise through the data.frame
# Need to get the dividend and divisor

# example data for part 2:
ex2 <- data.frame(
a = c(5, 9, 3),
b = c(9,4,8),
c = c(2,7,6),
d = c(8, 3, 5)
)

x <- ex2[1, ]

# I'll create an x by x matrix with every number in a vector divided by itself
# I want the numbers corresponding to matrix entries containing the sole non-1 integer and its reciprocal.
# Annoying, is.integer() doesn't do what I thought it would, so need to make my own function: https://stackoverflow.com/q/3476782/4470365
is_integer <- function(x) {
(x %% 1) == 0
}

is_recip_integer <- function(x){
((1/x) %% 1) == 0
}

get_quotient <- function(x){
# create x by x matrix dividing each entry by itself
divided <- sapply(x, function(a) a/x) %>%
unlist %>%
matrix(nrow = sqrt(length(.))) %>%
as.data.frame()

divided[divided == 1] <- NA # don't want the diagonal values

# Get the position where the vector contains an integer
bigger_index <- which(
lapply(divided, is_integer) %>%
lapply(sum, na.rm = TRUE) %>%
unlist(.) == 1) # I drafted this function as nested parentheses,
# converted to pipes for blogging... it looks weird.

# Get the position where the vector contains the reciprocal of an integer
smaller_index <- which(
lapply(divided, is_recip_integer) %>%
lapply(sum, na.rm = TRUE) %>%
unlist(.) == 1)

# Compute quotient of values at those indices
x[bigger_index] / x[smaller_index]

}

# Test
expect_equal(
# apply the function rowwise and sum
apply(ex2, 1, get_quotient) %>% sum,
9
)