On this page you’ll find a sampling of R and SQL code I’ve developed at work. All of the data is fake. To see the code, click on the buttons below the charts.
SQL used in Periscope to query the data:
with
base as (select * from [all_regions_and_quarters]),
data as
(
select
[so.finance_date__c:quarter] as q,
[user_subregion],
[arr]
from
salesforce.opportunity so
join salesforce.user su
on su.id = so.ownerid
left join salesforce.account sa
on sa.ownerid = su.id
where
region is not null
group by 1,2
order by 2,1
)
select
base.region,
base.quarter,
nvl(data.arr,0) as nacv,
row_number() over(
partition by base.region
order by base.quarter
) as f
from base
left join data
on
base.region = data.region
and base.quarter = data.q
where quarter between '2018.01.01' and '2020.01.01'
order by 1,2
Creating fake data: (Date format is what [so.finance_date__c:quarter]
outputs in Periscope SQL)
region <- c("AMS West", "Northern Europe", "Japan", "Federal", "AMS Northeast", "APAC", "AMS Southeast", "Southern Europe", "METNA", "AMS Central", "UK/I", "Central Europe")
quarter <- c("2018-01-01", "2018-04-01", "2018-07-01", "2018-10-01", "2019-01-01", "2019-04-01", "2019-07-01", "2019-10-01", "2020-01-01")
merge(region,quarter,by=NULL) %>%
rename(region=x, quarter=y) %>%
mutate_all(as.character) %>%
group_by(region) %>%
mutate(f = order(order(quarter))) -> df1
nice_q <- function(date){
year <- substr(date, 1, 4)
q <- substr(date, 6, 7)
q <- ifelse(q=='01',"Q1",ifelse(q=='04',"Q2",ifelse(q=='07',"Q3","Q4")))
paste(year,q)
}
df1$quarter %<>% map_chr(nice_q)
df1$nacv <- sample(0:100000, nrow(df1))
df1
The fake data:
Creating the plot:
df1 %>%
ggplot() +
geom_segment(aes(x=region, xend=region, y=0, yend=nacv, color=region, frame=quarter, size=I(10))) +
theme_bw()
ggplotly(tooltip=c("x", "yend", "frame"), hoveron="points+fills") %>%
animation_opts(1000, easing = "elastic", redraw = FALSE) %>%
animation_slider(currentvalue = list(prefix = "YEAR ", font = list(color=rgb(0,0,0,alpha=0)))) %>%
layout(xaxis = list(title = "", tickangle = 25), yaxis = list(title = "NACV"))
SQL used in Periscope to query the data:
select
sum(case when [so.finance_date__c:year] between 1999 and 2020
then nvl(so.churn__c,0) + nvl(so.narr__c,0) end) as beginning,
sum(case when [so.finance_date__c:quarter] = '2020.01.01'
then nvl(so.churn__c,0) + nvl(so.narr__c,0) end) as q1,
sum(case when [so.finance_date__c:quarter] = '2020.04.01'
then nvl(so.churn__c,0) + nvl(so.narr__c,0) end) as q2,
sum(case when [so.finance_date__c:quarter] = '2020.07.01'
then nvl(so.churn__c,0) + nvl(so.narr__c,0) end) as q3,
sum(case when [so.finance_date__c:quarter] = '2020.10.01'
then nvl(so.churn__c,0) + nvl(so.narr__c,0) end) as q4
from
salesforce.opportunity so
join salesforce.user su on
so.ownerid = su.id
where
so.isdeleted = false
and so.stagename in ('Closed Won', 'Closed Lost')
Creating fake data:
# Data that comes out of SQL is wide like this (each q its own column)
mm <- 10^6 # *mm = million
df <- tibble(
Start = 10*mm,
Q1 = 0.4*mm,
Q2 = 0.8*mm,
Q3 = 1.9*mm,
Q4 = 1.4*mm
)
The fake data:
Making the plot:
# Totals
df %<>%
melt(
variable.name='q',
value.name='arr',
measure.vars=colnames(df)
) %>%
adorn_totals("row")
df$id <- seq_along(df$arr) # id for later use
df$top <- cumsum(df$arr) # cumulative sum of amount
df$top <- c(head(df$top,-1),0) # same as above but ending with 0
df$bot <- c(0, head(df$top,-1)) # same as above but starting with 0
df[c(3,1,5,2,4)]->df # reorder columns
mutate(
df,
Goal=c(10*mm, 11*mm, 12*mm, 13*mm, 14*mm, 14*mm)
)->df
ggplotly(
ggplot(df) +
geom_rect(
aes(xmin=id-.4, xmax=id+.4, ymin=bot, ymax=top),
fill=rgb(111,185,255,maxColorValue = 255)
) +
geom_step(
aes(x=id+.5, y=Goal),
direction="vh",
color=rgb(136,74,165,maxColorValue = 255)
) +
scale_x_continuous(
breaks=c(1,2,3,4,5,6),
labels=c("Start","Q1","Q2","Q3","Q4","Cumulative")
) +
scale_y_continuous(
labels=dollar,
breaks=seq(0,60000000,5000000),
expand = c(0, 0)
)+
xlab("Quarter") + ylab("ARR") +
theme_bw() + theme(panel.border = element_blank())
) -> p
#Turn off hover info for line & bar
p$x$data[[1]]$hoverinfo <- "none"
p$x$data[[2]]$hoverinfo <- "none"
p
Creating fake data:
k <- 1000
df <- tibble(
Stage = map_chr(1:5,(function(i) paste("Stage",i))),
Users = c(95*k, 90*k, 85*k, 75*k, 70*k)
)
Creating the plot:
# Plot is made with ggplot2 then converted to plotly
# First: make the main bars with geom_rect()
# Then: create all the logic for the lighter-colored in-between trapezoids with geom_polygon() and poly df
# The 4 different poly dfs basically define one point in the trapezoid's shape
# When merged, they define 4 different trapezoids with 4 points each
# Alter df so that main bars can be made in geom_rect()
df <- df %>%
mutate(
`% of Total` = Users / first(Users),
`% Label` = paste(round((`% of Total` * 100),2), "%"),
`% of Previous` = replace_na((Users / lag(Users)), 1),
y_top = nrow(.) - row_number() + 3/4,
y_bot = nrow(.) - row_number()
)
# Start creating poly df so that in-between trapezoids can be made in geom_polygon()
# 4 different poly dfs each define one point of each trapezoid
poly1 <- df %>%
mutate(
y_top = y_bot
)
poly2 <- df %>%
mutate(
`% of Total` = 1 - `% of Total`,
y_top = y_bot
)
poly3 <- df %>%
mutate(
y_top = lead(y_top),
`% of Total` = lead(`% of Total`)
)
poly4 <- df %>%
mutate(
y_top = lead(y_top),
`% of Total` = 1 - lead(`% of Total`)
)
# Combine all 4 poly dfs
rbind(poly1, poly2, poly3, poly4) -> poly
poly <- poly %>%
arrange(`% of Total`)
# Make plot
ggplotly(
tooltip = c("xmax"),
ggplot(df) +
# The main rectangles
geom_rect(
aes(
xmax = `% of Total`,
xmin = 1 - `% of Total`,
ymax = y_top,
ymin = y_bot,
fill = Stage
)
) +
geom_polygon(
data=poly,
aes(
group=Stage,
x = `% of Total`,
y = y_top,
fill=Stage,
alpha = 1/2
)
) +
geom_text(
aes(
1/2, y_top - 3/8,
label= paste(Users, "\n % of Total:", `% Label`)
)
) +
scale_fill_brewer(palette="Set2") +
theme_void() +
theme(
legend.position = "right",
panel.grid.major = element_blank(),
axis.line = element_blank()
)
) #%>%
#periscope.plotly()