Over the past several months I’ve been building what’s become a large Google Sheet (which is now at the 2M cell limit) tracking Find&Save Cash Dash offers. Like a lot of data projects this one started out small but over many months grew to a point where it no longer makes sense to cut/paste SQL data into a Google Sheet for pivot tables and charts. I wanted a solution that could pull data from REST APIs, MSSQL and Postgres and present data in an internal dashboard. Unfortunately, Google Sheet’s JDBC support doesn’t include Postgres otherwise Google Apps Script might have been a viable choice for automating data collection notwithstanding size limitations. Finally, another issue with Sheets was that I couldn’t seem to get pivot tables to resize automatically as more data was added. So, 3 strikes and Google Sheets was out.
Part of building mobile web apps is understanding the myriad of mobile analytics and in part visualizing the data to shed light on trends that my otherwise be difficult to see in tabular data or even a colorful cohort table. I’ve been building a dashboard using R, RStudio, Shiny, and Shiny Dashboards aggregating data from MSSQL, Postgres, Google Analytics, and Localytics.
Below is the main function to fetch the Localytics sample data and convert it into a data frame that’s suitable for plotting. Now, admittedly I’m not an R expert so there may well be better ways to slice this JSON response but this is a fairly straight forward approach. Essentially, this fetches the data, converts it from JSON to an R object, extracts the weeks, preallocates a matrix and then iterates over the data filling the matrix to build a data frame.
retentionDF <- function() {
# Example data from: http://docs.localytics.com/dev/query-api.html#query-api-example-users-by-week-and-birth_week
localyticsExampleJSON <- getURL('https://gist.githubusercontent.com/strefethen/180efcc1ecda6a02b1351418e95d0a29/raw/1ad93c22488e48b5e62b017dc5428765c5c3ba0f/localyticsexampledata.json')
cohort <- fromJSON(localyticsExampleJSON)
weeks <- unique(cohort$results$week)
numweeks <- length(weeks)
# Take the JSON response and convert it to a retention matrix (all numeric for easy conversion to a dataframe) like so:
# Weekly.Cohort Users Week.1
# 1 2014-12-29 7187 4558
# 2 2015-01-05 5066 NA
i <- 1
# Create a matrix big enough to hold all of the data
m <- matrix(nrow=numweeks, ncol=numweeks + 1)
for (week in weeks) {
# Get data for all weeks of this cohort
d <- cohort$results[cohort$results$birth_week==week,][,2]
lencohort <- length(d)
for (n in 1:lencohort) {
# Skip the first column using "+ 1" below which will be Weekly.Cohort (date)
m[i,n + 1] <- d[n]
}
i <- i + 1
}
# Convert matrix to a dataframe
df <- as.data.frame(m)
# Set values of the first column to the cohort dates
df$V1 <- weeks
# Set the column names accordingly
colnames(df) <- c("Weekly.Cohort", "Users", paste0("Week.", rep(1:(numweeks-1))))
return(df)
}
To make things easy I put together a gist and if you’re using R you can runGist it yourself. It requires several other packages so be sure to check the sources in case you’re missing any. Fair warning the Localytics API demo has very limited data so the chart, let’s just say simplistic however given many weeks worth of data it will fill out nicely (see example below).
Short post to get this indexed as we didn’t find it elsewhere on the web.
While working to ship a new release of the Find&Save Android app we ran into a crash on launch which only occurred in our release build. The crash was in JSCExecutor.cpp which is part of React Native and left very few hints as to what was wrong with no call stack, Crashlytics logs etc. The only error we had was: ReactNativeJNI: Check failed: *m_isDestroyed JSCExecutor::destroy() must be called before its destructor Continue reading ReactNativeJNI: Check failed: *m_isDestroyed JSCExecutor::destroy() must be called before its destructor→
Blinding LED’s which fill the room with cool blue light.
A few weeks ago I decided to upgrade an aging
Motorola SB5101 cable modem to a new Motorola SURFboard 400 Series SB6141. After the obligatory call to Comcast to register the new MAC address everything was going swimmingly until I turned off the lights and suddenly landed in the world of TRON. The entire room was basked in cool blue light from the “stupidly, ridiculously, blindingly bright” LED’s. Seriously, they’re that bad and more, blinking incessantly and with a case full of holes it nearly filled the room. Continue reading Dimming the insanely bright LED’s on a Motorola SB6141 cable modem→
At work I’m in the midst of upgrading Find&Save to Xcode 7 and in this post I thought I would capture the issues I ran into while migrating our app to the latest build tools. I decided to keep my Xcode 6 install by renaming it “Xcode 6” and installing Xcode 7 along side it and all that worked pretty seamlessly (though keep reading because the AppStore updates will bite you). Once installed I opened our project workspace and hit Project | Build and thus began the journey of our migration. Continue reading Upgrading an iOS project from Xcode 6 to 7→
A few weeks ago my son woke me up early to remind me that we had a date to capture the sunrise on a timelapse in Santa Cruz.
Here are our results using a Sony a7II (390 photos): Continue reading Timelapse at Steamer Lane in Santa Cruz→
In XCode I ran into an error which was the result of a (my) bad merge that caused this problem when building an app:
/bin/sh: /Users/strefethen/github/iPhoneGeo/Build/iPhoneGeo/Build/Intermediates/iPhoneGeo.build/Debug-iphonesimulator/iPhoneGeo.build/Script-22C5773E1BB449FF00E72A71.sh: (null): bad interpreter: No such file or directory