Categories
#rstats

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!
my_input <- read.delim("02_1_dat.txt", header = FALSE)
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)
)

# start with the top row to play with
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
)

# Get part 2 answer:
apply(my_input, 1, get_quotient) %>% sum

 

Categories
#rstats

Advent of Code 2017 in #rstats: Day 1

Today’s puzzle is a good example of thinking “R-ishly”.   In R, I find it easier to compare vectors than to compare strings, and easier to work with vectorized functions than to loop.  So my code starts by splitting the input into a vector.

Part 1:

Split the input into a vector, add the first value to the end, and use dplyr’s lead() function to compare each value to the following value:

library(stringr)
library(dplyr)

target &lt;- "112345567891" # should yield 1 + 5 + 1 = 7
target &lt;- str_split(target, "") %&gt;% unlist %&gt;% as.numeric() # split input string into a vector:
target &lt;- c(target, target[1]) # add the first value to the end, to account for wrap-around
sum(target[target == lead(target)], na.rm = TRUE) # sum every number that matches the number following it

Part 2:

This challenge is simpler, in my opinion.  You can halve the vector, then compare the first half to the second half, summing the matches (don’t forget to double that result).

target &lt;- "123425" # should match both 2s for a total of 4
target &lt;- str_split(target, "") %&gt;% unlist %&gt;% as.numeric()
first_half &lt;- target[1:(length(target)/2)]
second_half &lt;- target[-c(1:(length(target)/2))]
sum(first_half[first_half == second_half], na.rm = TRUE) * 2
Categories
Beer Brewing process Homebrew

AABG Knob Creek Barrel Project

In December 2014, a friend of a friend acquired a 53 gallon barrel that had previously held Knob Creek Single Barrel Reserve whiskey for 9 years.  It made its way from Kentucky to Ann Arbor, then came along with me when I moved houses in 2018.  It currently resides in my basement.

Staining = spilled beer

Since then we’ve rotated beers through it.  In order of fill date:

  1. Imperial Stout (Jan 2015)
  2. Scotch Ale (this beer naturally soured)
  3. Oud Bruin (we pitched 8 packs of Blackman Flemish Sour Mix)
  4. Tart of Darkness Stout (here we introduced Brett C., actually a strain of Brett Anomalous)
  5. Dark Saison
  6. Belgian Golden Strong
  7. IPA
  8. Belgian Golden Strong, again (back by popular demand)
  9. Berliner Weisse (Jan 2019)
  10. Flanders Red (July 2019)
  11. Witbier (Jan 2020).

We empty + refill every six months or so.  The brewers are a rotating cast, with people dropping in and out.  We typically aim to bring 11 shares of 5 gallons each, filling the barrel to the top and leaving some extra to top up the angel’s share.

This barrel has produced consistently good beers and the sour character is now well-established.  Beers from this barrel have won silver and gold medals in the American Wild Ale category at the 2016 & 2017 Michigan Beer Cup.  Brewers often perform a tertiary fermentation on fruit – tart cherries are a favorite, this being Michigan – and sometimes blend with young or clean beers to cut sourness to taste.

Summer 2020 update: Due to COVID-19 it was not possible to empty and fill the barrel in summer 2020, an activity which requires several people in a close space.  We decided to switch to a solera project.  This beer will sit indefinitely, with participants having the option to extract 5 gallons of sour beer and swap in 5 gallons of fresh.  Such a swap only requires one person.

Whenever it again becomes possible to do another full empty/fill cycle, we will evaluate the barrel’s state.  In the meantime, what better substance to keep the barrel full and wet than beer?  Even if that beer ultimately becomes too sour to drink.

Categories
Brewing process Homebrew Other fermentables

How much sorbate and sulfite should you add to stabilize cider?

I re-researched this every fall when it came time to backsweeten the previous year’s cider, so I wrote this guide to future me.

If you add sugar to hard cider and don’t want that addition to restart fermentation (which would increase alcohol and leave the cider even drier), you’ll need to stabilize it.  The most common method in home cidermaking is to add both potassium sorbate and potassium metabisulfite (“K-Meta”).  This guidance from BYO magazine on backsweetening provides background on the approach.

Tl;dr

For each gallon of cider, use 1/2 tsp of potassium sorbate and 1/2 tsp of 10% sulfite solution (an extra step, but worth making – the solution is easier to work with than dry potassium metabisulfite).

Longer version

How much potassium sorbate?

Winemakers say…

Winemakers talk more about sorbic acid, the relevant chemical; potassium sorbate is 74% sorbic acid.  There are legal limits of 0.2 g/L (Europe) and 0.3 g/L (America); the sensory level for perceiving this chemical’s flavor is reported at 0.135 g/L.

Adjusting these from sorbic acid -> potassium sorbate (what a homebrewer weighs) gives legal limits of 0.26 g/L and 0.4 g/L, respectively, with a taste threshold of 0.18 g/L.

Categories
Biking Politics Science vs. Emotion

Ride Your Bike In Traffic and Live Longer

Summary: Your life expectancy is higher if you get in traffic on a bike instead of in a car.  Biking alongside cars might seem dangerous – and this misconception may deter potential cyclists or lead them to risky behavior like riding on the sidewalk – but the health benefits greatly exceed the dangers of crashes and other risks.

Updated May 2024 to address link rot.  This post now links to archived copies of an article by The Ann Magazine (whose website is gone, RIP) and a PDF about biking on the sidewalk.

—–

Last week, The Ann (an Ann Arbor magazine) showcased a story by a local NPR station about bikes and cars co-existing on the road.  The Ann added their own more-provocative title: “Who owns the road: drivers or cyclists?”

Their framing succeeded in drumming up conflict-oriented comments from readers.  Reading the comments, I was struck by two things:

Categories
#rstats Data analysis

How to Teach Yourself R

(Or, “how to teach professionals to teach themselves R”).

Background: I taught myself R in 2014 from public web resources, and since then have steered several cohorts of data analysts at my organization through various R curricula, adapting based on their feedback.

This is geared toward people teaching themselves R outside of graduate school (I perceive graduate students to have more built-in applications and more time for learning, though I don’t speak from experience).  I say “students” below but I am referring to professionals.  This advice assumes little or no programming experience in other languages, e.g., people making the shift from Excel to R (I maintain that Excel is one of R’s chief competitors).  If you already work in say, Stata, you may face fewer frustrations (and might consider DataCamp’s modules geared specifically to folks in your situation). 

I’ve tried combinations of Coursera’s Data Science Specialization, DataCamp’s R courses, and the “R for Data Science” textbook.  Here’s what I’ve learned about learning and teaching R and what I recommend.

I see three big things that will help you learn R:

  1. A problem you really want to solve
  2. A self-study resource
  3. A coach/community to help you
Categories
Beer Homebrew Recipe

Batch 73: Session Mild

This was simple: the second runnings from Batch 72.  The goal was two-fold:

  1. Have a light, approachable beer on tap for guests who drink “regular” beer
  2. A freebie: extra beer for little effort

Brewed March 24th, OG was ~1.036.  We threw in a partial ounce of random hops for bittering and used S-O4 dry yeast for low attenuation.

By April 7th the FG was 1.013 and I kegged it.  Roughly 3.0% ABV.

I served the beer from my cellar, at around 60 degrees, undercarbonated.  It was a little thin, though great for blending with other beers on tap.  For instance, blending it about 9:1 with my overly-sweet Belgian Dark Strong (~12%) on tap made for a highly drinkable session beer, leaving it slightly below 4% abv.

Like all of my second runnings beers, this keg blew way before the main batch – in this case, before the Imperial Stout was even packaged.

Categories
Beer Homebrew Recipe Uncategorized

Batch 72: Frank’s Imperial Stout

Having my brother in town as a helper, we decided to brew a partigyle beer: 11 gallons of Imperial Stout and a 2nd-runnings Mild.  I used the same recipe I came up with for the first beer in the Knob Creek barrel.  That beer was outstanding after blending with 10 other people’s beer and barrel-aging; as I recall, mine was pretty good going into the barrel, too.

The recipe was a mix of a few credible recipes.

Categories
Beer Homebrew Recipe

Batch 71: Scio Pilsner

There’s a closet in my basement that hovers near 50 degrees in the winter.  So before spring arrives, I wanted to take advantage of my natural “temperature control” and brew a lager.  I don’t brew many lagers, but the provocative Brulosophy experiments on lager yeast fermentation temperature gave me peace of mind that if a warm spell comes through and the room gets a little warmer, it’ll be fine.

This was a convenience recipe in other regards.  I used dry yeast to avoid needing a massive starter and I used up half a bag of leftover pils malt.  And I ran off 5 gallons of wort before adding flame-out hops, to ferment with an ale yeast and use to top up the 53 gallon barrel at my house that is mostly full of funky dark saison.  It’s nice to get rid of the headspace in the barrel, and we wager no one will notice 10% of hoppy Belgian ale blended in.

This is one of my favorite parts of using a plate chiller: being able to split batches by running off and chilling part of the brew, then adding and boiling as needed for the remaining share.

Condensation: the price of drinking cold beer in the warm sun

Categories
Homebrew Other fermentables

Batch 70: Eclipse Lodi Ranch 11 Cab

I’ve made country wine with Concord grapes, and wine from a kit that cost $2/bottle but tasted like $8/bottle.  But I’d rather drink beer than wine I can get for $8/bottle.  So I thought I’d try a kit that costs $6/bottle and see if it makes wine I actually want to drink.

In late 2016 I purchased  the Winexpert Eclipse Lodi Ranch 11 Cabernet Sauvignon kit.  Not sure what year that makes the grapes.  I’m not going to write much about ingredients or process since I followed the kit directions, unless otherwise noted.

2017-02-04: “Brewed” this with my 2 y/o son.  Despite the helper, kept good sanitation.  OG was around 1.093, though perhaps more sugars dissolved in from the grape skins.

Fermentation temperature bounced around from the minimum (72F) up to the mid-80s, as I crudely warmed it through a Michigan basement in winter.

I stirred the grape skin bag back down into the must, near-daily, for the first week.

2017-02-13 – 9 days: racked to a glass carboy.  Gravity is about .992!  Wine yeasts don’t play.