Skip to content

Instantly share code, notes, and snippets.

@zjuul
zjuul / default.yml
Created June 26, 2025 07:10
Espanso Big Query SQL Starter
matches:
- trigger: ":4"
replace: "GA4Dataform"
- trigger: ":micro"
replace: "timestamp_micros(event_timestamp) as ts"
- trigger: ":ts"
replace: "_table_suffix between '20210701' and '20210701'"
- trigger: ":sql"
replace: "select\n\tdate,\ncount(*) as n\n\bfrom x\ngroup by 1\norder by n desc"
- trigger: ":evs"
@zjuul
zjuul / sql_data_viz.sql
Last active April 10, 2025 02:48
Data visualisation in SQL
-- this query outputs a data visualisation to explore how many unique products people
-- see on your website, and the conversion rate
-- it uses Big Query SQL on a GA4Dataform events table - check https://github.com/superformlabs/ga4dataform-community
with products_per_user as (
SELECT
user_pseudo_id,
count( distinct if(event_name = 'view_item', i.item_name, NULL) ) as n_items_viewed,
max( if(event_name = 'purchase', 1, 0)) as purchaser
library(tidyverse)
richmondway <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2023/2023-09-26/richmondway.csv')
ggplot(
richmondway,
aes(
x = (max(richmondway$Episode) + 1) * Season + Episode,
y = F_count_RK
library(httr)
library(jsonlite)
library(lubridate)
library(tidyverse)
########### config
#
# my info - replace this with your enphase api key, app id, user_id
@zjuul
zjuul / add-missing-datalayer-event.js
Created September 14, 2020 09:08 — forked from sahava/add-missing-datalayer-event.js
This piece of code adds a default 'event' key-value to each dataLayer.push() that hasn't got one.
(function() {
var oldPush = window.dataLayer.push;
window.dataLayer.push = function() {
var states = [].slice.call(arguments, 0);
states.forEach(function(s) {
if (!s.hasOwnProperty('event')) {
s.event = 'default';
}
});
return oldPush.apply(window.dataLayer, states);
@zjuul
zjuul / wide_to_long.sql
Last active April 20, 2020 07:27
Transform your long data to wide data - to break down the request in phases
create or replace table `PROJECTNAME.DATASETNAME.performance_long` partition by DATE(timestamp) as
with base as (
select
timestamp, date, event_timestamp, clientid, user_id, device_category, country, url, hostname,
page_path, page_title, page_referrer, type
from `PROJECTNAME.DATASETNAME.performance_wide`
)
,intervals as (
@zjuul
zjuul / transform_to_wide.sql
Created April 14, 2020 20:18
Transform Performance Navigation data to wide format
create or replace table `PROJECTNAME.DATASETNAME.performance_wide` partition by DATE(timestamp) as
/*
wide version of nested events table
*/
SELECT
TIMESTAMP_MICROS(event_timestamp) as timestamp,
DATE(TIMESTAMP_MICROS(event_timestamp)) as date,
@zjuul
zjuul / tdf.R
Created July 9, 2019 15:11
Tour de France 2019 - plot general classification
library(httr)
library(jsonlite)
library(ggplot2)
library(dplyr)
# url with TDF standings
url <- "https://sportapi.widgets.sports.gracenote.com/cycling/getresult/classificationid/2148837/languagecode/1.json?c=64&module=cycling&type=classification"
standings <- fromJSON(content(GET(url), "text")[[1]])
// GTM custom HTML tag for tracking incomplete searches
// logic: if user starts typing, start a timer. Stop the timer when user is idle 2.5 seconds
// then push the event on the DL
(function() {
var timer, firstResult, searchText;
var waiting = false;
var waitTime = 2500; // milliseconds
@zjuul
zjuul / all_join_types.sql
Created August 23, 2018 13:58
SQL joins: all in one
-- create two tables: L and R
-- content of tables a "val" column with two rows.
-- rows in L: "left only" and "both"
-- rows in R: "right only" and "both"
with l as (
select 'both' as val
union
select 'left_only' as val
), r as (