table(aja$solarAPP_or_traditional)
library(lubridate)
head(aja$permit_submission_date)
ajb<-aja %>% mutate(date=as.Date(permit_submission_date,"%m/%d/%y")) %>% mutate(year=year(date))
ajb<-aja %>% mutate(date=as.Date(permit_submission_date,"%m/%d/%y")) %>%
mutate(year=year(date),sapp=ifelse(solarAPP_or_traditional%in%c("traditional","Traditional"),0,1))
sum(ajb$sapp)
13820+8986+10921
mean(is.na(aja$solarAPP_or_traditional))
View(aja)
ajb<-aja %>% mutate(date=as.Date(permit_submission_date,"%m/%d/%y")) %>%
mutate(year=year(date),sapp=ifelse(solarAPP_or_traditional%in%c("traditional","Traditional"),0,1)) %>%
filter(!is.na(solarAPP_or_traditional))
table(ajb$sapp[ajb$year==2021])
table(ajb$sapp[ajb$year==2022])
table(ajb$sapp[ajb$year==2023])
table(ajb$sapp[ajb$year==2024])
ajb<-aja %>% mutate(date=as.Date(permit_submission_date,"%m/%d/%y")) %>%
mutate(year=year(date),sapp=ifelse(solarAPP_or_traditional%in%c("traditional","Traditional"),0,1))# %>%
wtf<-filter(ajb,year==2021)
View(wtf)
wtf2<-filter(ajb,origin==21)
table(wtf2$solarAPP_or_traditional)
View(wtf2)
table(aja$origin)
'slateblue'
dba<-read_csv("/Volumes/Expansion/TTS/2025/Demographics/db_viz_slim.csv") # use this to tag on county and tract IDs
library(readr)
dba<-read_csv("/Volumes/Expansion/TTS/2025/Demographics/db_viz_slim.csv") # use this to tag on county and tract IDs
View(dba)
library(shiny)
runApp()
116/3
22/39
cfa<-data.frame(mw=c(2690.2,79883.9,98022.9,4054.5,1685.1,102319.5,1392,149299,7476),
cf=c(64.6,34.6,90.8,59.5,49,23.2,25,34.3,55.8))
weighted.mean(cfa$cf,cfa$mw)
library(tidyverse)
library(tidycensus)
sta <- read.csv("~/Desktop/Data/Geo/state_dta.csv")
census_api_key("e32e803473f309d9e95fc35d8c56c7290956dd36")
# DP02_0001E ~ Total households
# DP03_0062E ~ Median Income
zp<-get_acs(geography="us",variables=c("DP03_0001E","DP03_0063E"),year=2023)
zp
# DP02_0001E ~ Total households
# DP03_0062E ~ Median Income
zp<-get_acs(geography="us",variables=c("DP03_0051E","DP03_0063E"),year=2023)
zp
0.3*100*8760
library(shiny)
runApp()
library(readxl)
olda <- read_excel("Downloads/MA_DOER_012325.xlsx")
View(olda)
library(readxl)
nwa <- read_excel("Downloads/SMART_LBNL_020526.xlsx")
library(tidyverse)
wtf<-filter(olda,!(`Project Number`%in%nwa$`Project Number`))
View(wtf)
head(olda$`Project Number`)
head(nwa$`Project Number`)
olda<-mutate(olda,prj=substr(`Project Number`,1,4))
nwa<-mutate(nwa,prj=substr(`Project Number`,1,4))
wtf<-mutate(wtf,prj=substr(`Project Number`,1,4))
table(olda$prj)
table(nwa$prj)
table(wtf$prj)
View(wtf)
51650-51518
length(unique(nwa$`Project Number`))
wtf<-filter(olda,!(`Project Number`%in%nwa$`Project Number`))
write.csv(wtf,"~/Desktop/SMART_missing_records.csv",row.names=F)
25184+69475
69475-25184
library(shiny)
runApp()
1860/900
(1860*1.4)/(900*1.4)
4750+14250+6000
4750+14250
6000+6000+1500
24000-13500
235/42.2
205/42.2
155/42.2
0.67*60
170/42.2
0.028*60
30*12*20
20*193
60*193
100*0.3*30
100*0.3*30*8760
100*1*30*8760
100*0.3*1*8760
100*0.3*2*8760
1.3*240
312/60
0.2*60
source("~/Desktop/Data/prep.R")
library(readr)
setwd("~/Desktop/Loans/")
### Section 3.4 industry
dta <- read_csv("/Volumes/Expansion/TTS/2025/FA/finance_flags_10_15_25.csv")
ita<-dta %>% filter(installer!="-9999") %>%
mutate(n=1,
fnc=ifelse(loan==1,"loan",
ifelse(tpo==1,"tpo","cash")))
# calculate: 1) average size; 2) top 10 share; 3) HHI; 4) % new
# average size
sz<-function(dd,S) {
a<-dd %>%
inner_join(aggregate(n~installer+year,data=dd,sum) %>% rename(instN=n)) %>%
inner_join(aggregate(n~year,data=dd,sum) %>% rename(N=n))
b<-aggregate(instN~fnc+year,data=a,mean) %>% mutate(instN=instN/1000)
colnames(b)<-c("fnc","year",S)
return(b)
}
sza<-inner_join(sz(ita,"fl"),sz(filter(ita,cl=="H"),"H"))
# installer size stat
# top 10 share
t10<-ita %>% group_by(installer,year) %>% summarize(n=sum(n)) %>%
arrange(year,-n) %>% mutate(i=1) %>% group_by(year) %>%
mutate(j=cumsum(i)) %>% mutate(top10=ifelse(j<=10,1,0))
t10f<-function(dd,S) {
a<-dd %>% inner_join(select(t10,installer,year,top10)) %>%
group_by(year,fnc) %>% summarize(top10=mean(top10)) %>%
mutate(top10=top10*100)
colnames(a)<-c("year","fnc",S)
return(a)
}
t10a<-inner_join(t10f(ita,"fl"),t10f(filter(ita,cl=="H"),"H"))
# HHI
hhf<-function(dd,S) {
a<-dd %>% group_by(installer,fnc,year) %>%
summarize(instN=sum(n)) %>%
inner_join(aggregate(n~fnc+year,data=dd,sum) %>% rename(N=n)) %>%
mutate(share=(instN/N)) %>% group_by(fnc,year) %>%
summarize(hhi=sum(share^2))
colnames(a)<-c("fnc","year",S)
return(a)
}
hha<-inner_join(hhf(ita,"fl"),hhf(filter(ita,cl=="H"),"H"))
# new installers
nwa<-ita %>%
inner_join(aggregate(year~installer,data=ita,min) %>% rename(startYear=year)) %>%
mutate(entry=ifelse(year==startYear,1,0))
nwf<-function(dd,S) {
a<-dd %>% inner_join(unique(select(nwa,ID_Master,entry))) %>% group_by(fnc,year) %>%
summarize(entry=mean(entry)) %>% filter(year>2017) %>% mutate(entry=entry*100)
colnames(a)<-c("fnc","year",S)
return(a)
}
nwi<-inner_join(nwf(ita,"fl"),nwf(filter(ita,cl=="H"),"H"))
head(sza)
head(t10a)
names(sza)
names(nwi)
names(t10a)
names(hha)
ixpt<-rbind(mutate(sza,stat="installer size"),
mutate(nwi,stat="new installers"),
mutate(t10a,stat="top 10 share"),
mutate(hha,stat="hhi"))
head(ixpt)
write.csv(ixpt,"/Volumes/Expansion/EREN_data_scripts/figure9.csv",row.names=F)
# storage trends
stra <- read_csv("/Volumes/Expansion/TTS/2025/Core/storage_PVsize_dataset.csv")
stta<-dta %>% inner_join(select(stra,ID_Master,storage_flag)) %>%
mutate(n=1,fnc=ifelse(loan==1,"loan",ifelse(tpo==1,"tpo","cash")))
strf<-function(CL) {
stta %>% filter(cl%in%CL) %>% group_by(fnc,year) %>%
summarize(storage=mean(storage_flag*100))
}
sttf<-strf(c("M","H")) %>% inner_join(strf("H") %>% rename(hc=storage)) %>%
mutate(lo=ifelse(storage<hc,storage,hc),hi=ifelse(storage>hc,storage,hc))
head(sttf)
write.csv(sttf,"/Volumes/Expansion/EREN_data_scripts/figure10.csv",row.names=F)
source("prep.R")
setwd("/Volumes/Expansion/EREN_data_scripts/")
rm(list=ls())
source("prep.R")
setwd("~/Desktop/Loans/")
# set working directory to the data repositor
### setwd("YOUR WD")
f9<-read.csv("Data/figure9.csv")
setwd("/Volumes/Expansion/EREN_data_scripts/")
# set working directory to the data repositor
### setwd("YOUR WD")
f9<-read.csv("Data/figure9.csv")
fgr<-function(mm,ttl,yL,yM) {
fga<-filter(f9,metric==mm) %>% mutate(lo=ifelse(fl<H,fl,H),hi=ifelse(fl>H,fl,H))
a<-base(fga)+ggtitle(ttl)+coord_cartesian(ylim=c(0,yM))+
geom_ribbon(aes(x=year,ymin=lo,ymax=hi,fill=fnc),alpha=0.6)+
geom_line(aes(year,fl,color=fnc))+
geom_line(aes(year,H,color=fnc),linetype='dashed')+
scale_color_manual(values=c('darkcyan','navy','tan1'),guide="none")+
scale_fill_manual(values=c('darkcyan','navy','tan1'),guide="none")+
#scale_shape_manual(values=c(16:18),guide="none")+
ylab(yL)+theme(axis.title.x=element_blank())
return(a)
}
table(f9$stat)
fgr<-function(mm,ttl,yL,yM) {
fga<-filter(f9,stat==mm) %>% mutate(lo=ifelse(fl<H,fl,H),hi=ifelse(fl>H,fl,H))
a<-base(fga)+ggtitle(ttl)+coord_cartesian(ylim=c(0,yM))+
geom_ribbon(aes(x=year,ymin=lo,ymax=hi,fill=fnc),alpha=0.6)+
geom_line(aes(year,fl,color=fnc))+
geom_line(aes(year,H,color=fnc),linetype='dashed')+
scale_color_manual(values=c('darkcyan','navy','tan1'),guide="none")+
scale_fill_manual(values=c('darkcyan','navy','tan1'),guide="none")+
#scale_shape_manual(values=c(16:18),guide="none")+
ylab(yL)+theme(axis.title.x=element_blank())
return(a)
}
# plot all of that
ggsave("Figures/industry_effects.jpg",
plot=plot_grid(fgr("installer size","a) Average Installer Size","N\n(x1000)",40)+
ant("TPO",2019,20,4.5,'firebrick4')+ant("Loan",2022,12,4.5,'navy')+
ant("Customer financed",2019,2,4,'darkcyan')+
geom_segment(x=2017,xend=2017.8,y=38,yend=38,color='gray50')+ant("Full sample",2019.1,38,4,'gray50')+
geom_segment(x=2017,xend=2017.8,y=35,yend=35,color='gray50',linetype="dashed")+ant("High confidence",2019.55,35,4,'gray50'),
fgr("new installers","b) New Installer Share","   %   ",4)+
coord_cartesian(xlim=c(2017,2024)),
fgr("top 10 share","c) Top 10 Installer Share","   %  ",80),
fgr("hhi","d) Market Concentration (HHI)","HHI",0.26)+
scale_y_continuous(breaks=c(0,0.13,0.26),labels=c("0","0.13","0.26")),nrow=2),width=8,height=8)
# plot all of that
ggsave("Figures/figure9.jpg",
plot=plot_grid(fgr("installer size","a) Average Installer Size","N\n(x1000)",40)+
ant("TPO",2019,20,4.5,'firebrick4')+ant("Loan",2022,12,4.5,'navy')+
ant("Customer financed",2019,2,4,'darkcyan')+
geom_segment(x=2017,xend=2017.8,y=38,yend=38,color='gray50')+ant("Full sample",2019.1,38,4,'gray50')+
geom_segment(x=2017,xend=2017.8,y=35,yend=35,color='gray50',linetype="dashed")+ant("High confidence",2019.55,35,4,'gray50'),
fgr("new installers","b) New Installer Share","   %   ",4)+
coord_cartesian(xlim=c(2017,2024)),
fgr("top 10 share","c) Top 10 Installer Share","   %  ",80),
fgr("hhi","d) Market Concentration (HHI)","HHI",0.26)+
scale_y_continuous(breaks=c(0,0.13,0.26),labels=c("0","0.13","0.26")),nrow=2),width=8,height=8)
# Figure 10
f10<-read.csv("Figures/figure10.csv")
ggsave("Figures/storage.jpg",
plot=base(f10)+
geom_ribbon(aes(x=year,ymin=lo,ymax=hi,fill=fnc),alpha=0.6)+
geom_line(aes(year,storage,color=fnc))+
geom_line(aes(year,hc,color=fnc),linetype='dashed')+
scale_color_manual(values=c('darkcyan','navy','tan1'),guide="none")+
scale_fill_manual(values=c('darkcyan','navy','tan1'),guide="none")+
ylab("Storage\nAttachment\nRate\n(%)")+theme(axis.title.x=element_blank())+
ant("TPO",2023,30,5,'firebrick4')+ant("Loan",2018,6,5,'navy')+
ant("Customer financed",2022.5,2.5,5,'darkcyan'),width=5,height=5)
# Figure 10
f10<-read.csv("Data/figure10.csv")
ggsave("Figures/storage.jpg",
plot=base(f10)+
geom_ribbon(aes(x=year,ymin=lo,ymax=hi,fill=fnc),alpha=0.6)+
geom_line(aes(year,storage,color=fnc))+
geom_line(aes(year,hc,color=fnc),linetype='dashed')+
scale_color_manual(values=c('darkcyan','navy','tan1'),guide="none")+
scale_fill_manual(values=c('darkcyan','navy','tan1'),guide="none")+
ylab("Storage\nAttachment\nRate\n(%)")+theme(axis.title.x=element_blank())+
ant("TPO",2023,30,5,'firebrick4')+ant("Loan",2018,6,5,'navy')+
ant("Customer financed",2022.5,2.5,5,'darkcyan'),width=5,height=5)
ggsave("Figures/figure10.jpg",
plot=base(f10)+
geom_ribbon(aes(x=year,ymin=lo,ymax=hi,fill=fnc),alpha=0.6)+
geom_line(aes(year,storage,color=fnc))+
geom_line(aes(year,hc,color=fnc),linetype='dashed')+
scale_color_manual(values=c('darkcyan','navy','tan1'),guide="none")+
scale_fill_manual(values=c('darkcyan','navy','tan1'),guide="none")+
ylab("Storage\nAttachment\nRate\n(%)")+theme(axis.title.x=element_blank())+
ant("TPO",2023,30,5,'firebrick4')+ant("Loan",2018,6,5,'navy')+
ant("Customer financed",2022.5,2.5,5,'darkcyan'),width=5,height=5)
rm(list=ls())
source("prep.R")
# set working directory to the data repositor
### setwd("YOUR WD")
ffa <- read_csv("Data/public_data.csv")
c(nrow(ffa),sum(ffa$cl=="H")) # two sample sizes
qtrs<-data.frame(month=rep(c(1:12),times=8),year=rep(c(2017:2024),each=12),qtr=rep(c(1:32),each=3))
# interest rates for plot
fra <- read.csv("Data/personal_loan_finance_rate.csv") %>%
mutate(date=as.Date(observation_date)) %>% # https://fred.stlouisfed.org/graph/?id=TERMCBPER24NS,
mutate(month=month(date),year=year(date)) %>% inner_join(qtrs) %>%
group_by(qtr) %>% summarize(intRate=mean(TERMCBPER24NS,na.rm=T)) %>% mutate(rsRate=intRate*5)
fcln<-function(cll,lb) {
a<-ffa %>% filter(cl%in%cll) %>%
group_by(qtr,year) %>% summarize(loan=mean(loan*100),tpo=mean(tpo*100),cash=mean(cash*100))
colnames(a)<-c("qtr","year",paste0(c("loan","tpo","cash"),lb))
return(a)
}
fia<-fcln(c("H","M","L"),"") %>% inner_join(fcln("H","H")) %>% arrange(qtr)
fta<-data.frame(fnc=rep(c("loan","tpo","cash"),each=nrow(fia)),qtr=rep(fia$qtr,times=3),
full=c(fia$loan,fia$tpo,fia$cash),high=c(fia$loanH,fia$tpoH,fia$cashH)) %>%
mutate(lo=ifelse(full<high,full,high),hi=ifelse(full>high,full,high))
# combined figure
ggsave("Figures/figure3.jpg",
plot=base(fta)+coord_cartesian(ylim=c(0,70))+
geom_ribbon(aes(x=qtr,ymin=lo,ymax=hi,fill=fnc),alpha=0.6)+
geom_line(aes(qtr,full,color=fnc))+geom_line(aes(qtr,high,color=fnc),linetype='dashed')+
geom_line(data=fra,aes(qtr,rsRate),color='firebrick4',linetype='dotted')+
theme(axis.title.x=element_blank())+ylab("Market\nShare\n(%)")+
scale_fill_manual(values=c("darkcyan","navy","tan1"),guide="none")+
scale_color_manual(values=c("darkcyan","navy","tan1"),guide="none")+
ant("Loans",17,67,5,'navy')+ant("TPO",5,57,5,'tan1')+
ant("Customer",28,3.5,5,'darkcyan')+ant("financed",28,-0.5,5,'darkcyan')+
ant("10.1%",2/2,47,3.5,'firebrick4')+ant("12.3%",31.5,58,3.5,'firebrick4')+
ant("Personal loan",30,69,3,'firebrick4')+ant("finance rate",30,66,3,'firebrick4')+
scale_x_continuous(breaks=c(5,13,21,29),labels=c(2018,2020,2022,2024))+
geom_segment(x=1,xend=4,y=70,yend=70,color='gray50')+ant("Full sample",7,70,4,'gray50')+
geom_segment(x=1,xend=4,y=65,yend=65,color='gray50',linetype="dashed")+ant("High confidence",8.1,65,4,'gray50')+
scale_y_continuous(breaks=c(0,35,70)),width=6,height=4)
# breakdowns by filer types
c(mean(ffa$mjr),mean(ffa$mjr[ffa$cl=="H"])) # mjr = PV loan specialist
c(mean(ffa$bank),mean(ffa$bank[ffa$cl=="H"]))
ffa <- read_csv("/Volumes/Expansion/TTS/2025/FA/finance_flags_10_15_25.csv")
c(nrow(ffa),sum(ffa$cl=="H")) # two sample sizes
lta <- read.csv("Data/finance_flag_counts_10_15_25.csv")
setwd("~/Desktop/Loans/")
wmc<-read.csv("Data/woodmac_finance_shares.csv")# warning is ok
wmf<-as.data.frame(wmc %>% pivot_longer(!type,names_to="year",values_to="mw")) %>%
mutate(year=as.numeric(substr(year,2,5))) %>% inner_join(aggregate(mw~year,data=.,sum) %>% rename(total=mw)) %>%
mutate(share=(mw/total)*100)
wmf # WoodMac stats for intro
# correlation with interest rates
qtrs<-data.frame(month=rep(c(1:12),times=8),year=rep(c(2017:2024),each=12),qtr=rep(c(1:32),each=3))
fda<-read.csv("Data/FEDFUNDS.csv") %>%
mutate(date=as.Date(observation_date)) %>%
mutate(month=month(date),year=year(date)) %>% inner_join(qtrs) %>%
group_by(qtr) %>% summarize(intRate=mean(FEDFUNDS)) %>% mutate(rsRate=intRate*12)
fra <- read.csv("~/Desktop/Loans/Data/personal_loan_finance_rate.csv") %>%
mutate(date=as.Date(observation_date)) %>% # https://fred.stlouisfed.org/graph/?id=TERMCBPER24NS,
mutate(month=month(date),year=year(date)) %>% inner_join(qtrs) %>%
group_by(qtr) %>% summarize(intRate=mean(TERMCBPER24NS,na.rm=T)) %>% mutate(rsRate=intRate*5)
# breakdowns by filer types
mjrs<-c("dividend","enfin","enium","goodleap","sunnova","solar mosaic","sunlight financial")
# majors, banks, and credit unions
lfa<-ffa %>% mutate(i=c(1:nrow(.)),mjr=ifelse(filer%in%mjrs,1,0)) %>%
mutate(bank=ifelse(i%in%agrep("bank",filer,max.distance=0),1,0),
cu=ifelse(i%in%agrep("credit union",filer,max.distance=0),1,0)) %>%
mutate(any=ifelse(mjr==1 | bank==1 | cu==1,1,0)) %>% filter(loan==1)
c(mean(lfa$mjr),mean(lfa$mjr[lfa$cl=="H"]))
rm(list=ls())
setwd("/Volumes/Expansion/EREN_data_scripts/")
source("prep.R")
# set working directory to the data repositor
### setwd("YOUR WD")
ffa <- read_csv("Data/public_data.csv")
c(nrow(ffa),sum(ffa$cl=="H")) # two sample sizes
qtrs<-data.frame(month=rep(c(1:12),times=8),year=rep(c(2017:2024),each=12),qtr=rep(c(1:32),each=3))
# interest rates for plot
fra <- read.csv("Data/personal_loan_finance_rate.csv") %>%
mutate(date=as.Date(observation_date)) %>% # https://fred.stlouisfed.org/graph/?id=TERMCBPER24NS,
mutate(month=month(date),year=year(date)) %>% inner_join(qtrs) %>%
group_by(qtr) %>% summarize(intRate=mean(TERMCBPER24NS,na.rm=T)) %>% mutate(rsRate=intRate*5)
fcln<-function(cll,lb) {
a<-ffa %>% filter(cl%in%cll) %>%
group_by(qtr,year) %>% summarize(loan=mean(loan*100),tpo=mean(tpo*100),cash=mean(cash*100))
colnames(a)<-c("qtr","year",paste0(c("loan","tpo","cash"),lb))
return(a)
}
fia<-fcln(c("H","M","L"),"") %>% inner_join(fcln("H","H")) %>% arrange(qtr)
fta<-data.frame(fnc=rep(c("loan","tpo","cash"),each=nrow(fia)),qtr=rep(fia$qtr,times=3),
full=c(fia$loan,fia$tpo,fia$cash),high=c(fia$loanH,fia$tpoH,fia$cashH)) %>%
mutate(lo=ifelse(full<high,full,high),hi=ifelse(full>high,full,high))
# combined figure
ggsave("Figures/figure3.jpg",
plot=base(fta)+coord_cartesian(ylim=c(0,70))+
geom_ribbon(aes(x=qtr,ymin=lo,ymax=hi,fill=fnc),alpha=0.6)+
geom_line(aes(qtr,full,color=fnc))+geom_line(aes(qtr,high,color=fnc),linetype='dashed')+
geom_line(data=fra,aes(qtr,rsRate),color='firebrick4',linetype='dotted')+
theme(axis.title.x=element_blank())+ylab("Market\nShare\n(%)")+
scale_fill_manual(values=c("darkcyan","navy","tan1"),guide="none")+
scale_color_manual(values=c("darkcyan","navy","tan1"),guide="none")+
ant("Loans",17,67,5,'navy')+ant("TPO",5,57,5,'tan1')+
ant("Customer",28,3.5,5,'darkcyan')+ant("financed",28,-0.5,5,'darkcyan')+
ant("10.1%",2/2,47,3.5,'firebrick4')+ant("12.3%",31.5,58,3.5,'firebrick4')+
ant("Personal loan",30,69,3,'firebrick4')+ant("finance rate",30,66,3,'firebrick4')+
scale_x_continuous(breaks=c(5,13,21,29),labels=c(2018,2020,2022,2024))+
geom_segment(x=1,xend=4,y=70,yend=70,color='gray50')+ant("Full sample",7,70,4,'gray50')+
geom_segment(x=1,xend=4,y=65,yend=65,color='gray50',linetype="dashed")+ant("High confidence",8.1,65,4,'gray50')+
scale_y_continuous(breaks=c(0,35,70)),width=6,height=4)
# breakdowns by filer types
lfa<-filter(ffa,loan==1)
c(mean(lfa$mjr),mean(ffa$mjr[lfa$cl=="H"])) # mjr = PV loan specialist
c(mean(lfa$bank),mean(ffa$bank[lfa$cl=="H"]))
c(mean(lfa$cu),mean(ffa$cu[lfa$cl=="H"])) # cu = credit union
c(mean(lfa$mjr),mean(lfa$mjr[lfa$cl=="H"])) # mjr = PV loan specialist
c(mean(lfa$bank),mean(lfa$bank[lfa$cl=="H"]))
c(mean(lfa$cu),mean(lfa$cu[lfa$cl=="H"])) # cu = credit union
aggregate(any~year,data=filter(lfa,cl=="H"),mean) # any = any of PV loan specialist, bank, or credit union
#
lya<-lfa %>% group_by(year) %>% summarize(mjr=mean(mjr*100),bank=mean(bank*100),cu=mean(cu*100)) %>%
inner_join(filter(lfa,cl=="H") %>% group_by(year) %>% summarize(mjrH=mean(mjr*100),bankH=mean(bank*100),cuH=mean(cu*100)))
lffa<-data.frame(year=rep(lya$year,times=3),typ=rep(c("aMjr","bBank","cCu"),each=nrow(lya)),
full=c(lya$mjr,lya$bank,lya$cu),high=c(lya$mjrH,lya$bankH,lya$cuH)) %>%
mutate(lo=ifelse(full<high,full,high),hi=ifelse(full>high,full,high))
lbz<-data.frame(lb=c("PV loan specialists","Banks","Credit","unions"),typ=c("aMjr","bBank","cCu","cCu"),
x=c(2021,2023.1,2020.8,2020.8),y=c(50,20.5,21,17))
ggsave("Figures/figure4.jpg",
plot=base(lffa)+coord_cartesian(ylim=c(0,75))+
geom_ribbon(aes(year,ymin=lo,ymax=hi,fill=typ),alpha=0.6)+
geom_line(aes(year,full,color=typ))+geom_line(aes(year,high,color=typ),linetype='dashed')+
geom_text(data=lbz,aes(x,y,label=lb,color=typ),size=5)+
scale_fill_manual(values=c("slateblue","purple","darkorange"),guide="none")+
scale_color_manual(values=c("slateblue","purple","darkorange4"),guide="none")+
ylab("PV Loan\nMarket\nShare\n(%)")+scale_y_continuous(breaks=c(0,25,50,75))+
geom_segment(x=2020,xend=2021,y=77,yend=77,color='gray50')+ant("Full sample",2022.25,77,4,'gray50')+
geom_segment(x=2020,xend=2021,y=74,yend=74,color='gray50',linetype="dashed")+ant("High confidence",2022.7,74,4,'gray50')+
theme(axis.title.x=element_blank()),width=4,height=4)
rm(list=ls())
source("prep.R")
# set working directory to the data repositor
### setwd("YOUR WD")
# Figure 5
f5<-read.csv("Data/figure5.csv")
pfgr<-function(ttl,yl,mv,p20v,p80v,ym) {
a<-f5 %>% mutate(md=mv,lo=p20v,hi=p80v)
b<-base(a)+ggtitle(ttl)+coord_cartesian(xlim=c(0.5,3.5),ylim=c(0,ym))+
geom_segment(aes(x=x,xend=x,y=lo,yend=hi,color=xpos))+
geom_point(aes(x=x,y=md,color=xpos,shape=smpl),fill="white",size=6)+
scale_color_manual(values=c("tan1","navy","darkcyan"),guide="none")+
scale_shape_manual(values=c(21,16),name="Sample:",labels=c("Full","High confidence"))+ylab(yl)+
scale_x_continuous(breaks=c(1:3),labels=c("TPO","Loan","Customer\nFinanced"))+
theme(axis.title.x=element_blank(),legend.position="bottom")
return(b)
}
ggsave("Figures/figure5.jpg",
plot=plot_grid(pfgr("Adopter Income","$1000",f5$mdInc,f5$p20Inc,f5$p80Inc,250)+
theme(axis.text.x=element_blank(),legend.position="none")+
ant("p20",0.63,67,4,'gray50')+ant("p50",0.63,122,4,'gray50')+ant("p80",0.63,195,4,'gray50'),
pfgr("Adopter Relative Income","    %  ",f5$mdRel,f5$p20Rel,f5$p80Rel,240),nrow=2,rel_heights=c(60,75)),width=4.5,height=8.5)
# Figure 6
f6<-read.csv("Data/figure6.csv")
ggsave("Figures/figure6.jpg",
base(f6)+coord_cartesian(ylim=c(100,180))+
geom_ribbon(aes(x=year,ymin=lo,ymax=hi,fill=fnc),alpha=0.6)+
geom_line(aes(year,rel,color=fnc))+
geom_line(aes(year,relH,color=fnc),linetype='dashed')+
scale_color_manual(values=c('darkcyan','navy','tan1'),guide="none")+
scale_fill_manual(values=c('darkcyan','navy','tan1'),guide="none")+
ant("Customer financed",2021,170,5,'darkcyan')+ant("Loans",2019,146,5,'navy')+
ant("TPO",2023,140,5,'tan1')+
geom_segment(x=2017,xend=2017.8,y=110,yend=110,color='gray50')+ant("Full sample",2018.7,110,4,'gray50')+
geom_segment(x=2017,xend=2017.8,y=105,yend=105,color='gray50',linetype="dashed")+ant("High confidence",2019,105,4,'gray50')+
ylab("Adopter Relative\nIncome (%)")+
theme(axis.title.x=element_blank()),width=6,height=4)
# Figure 7
f7<-read.csv("Data/figure7.csv")
bfgr<-function(ss,ttl,xl) {
base(filter(f7,smpl==ss))+ggtitle(ttl)+
coord_cartesian(xlim=c(0.5,xl))+
geom_col(aes(bin,pct,fill=fnc),position='stack')+
scale_fill_manual(values=c("darkcyan","navy","tan1"),guide="none")+
scale_y_continuous(breaks=seq(0,80,20))+
geom_text(aes(bin,ly,label=label),color='white',size=4)+
ylab("Share of\nAdopters\n(%)")+
scale_x_continuous(breaks=c(1:4),
labels=c("<50%","50-\n100%","100-\n150%",">150%"))+
theme(axis.title.x=element_blank())
}
rm(list=ls())
source("prep.R")
# set working directory to the data repositor
### setwd("YOUR WD")
fta <- read_csv("Data/public_price_data.csv") # note this file already excludes TPO systems
c(nrow(fta),sum(fta$cl=="H")) # sample sizes
# t tests based on 2024 data
t.test(install_price_real_w~loan,data=filter(fta,year==2024))
t.test(install_price_real_w~loan,data=filter(fta,year==2024,cl=="H"))
# figure by year
ffa<-aggregate(install_price_real_w~year+loan,data=fta,mean) %>%
inner_join(aggregate(install_price_real_w~year+loan,data=filter(fta,cl=="H"),mean) %>% rename(hc=install_price_real_w)) %>%
mutate(lo=ifelse(install_price_real_w<hc,install_price_real_w,hc),
hi=ifelse(install_price_real_w>hc,install_price_real_w,hc))
ggsave("Figures/figure8.jpg",
plot=base(ffa)+coord_cartesian(ylim=c(0,6))+
geom_ribbon(aes(x=year,ymin=lo,ymax=hi,fill=factor(loan)),alpha=0.6)+
geom_line(aes(year,install_price_real_w,color=factor(loan)))+
geom_line(aes(year,hc,color=factor(loan)),linetype='dashed')+
ylab("Average\nPrice\n($/W)")+
theme(axis.title.x=element_blank())+
ant("Customer financed",2019,4,5,'darkcyan')+
ant("Loans",2022,5.5,5,"navy")+
scale_fill_manual(values=c("darkcyan",'navy'),guide="none")+
scale_color_manual(values=c("darkcyan","navy"),guide="none")+
geom_segment(x=2017.4,xend=2018.2,y=1.4,yend=1.4,color='gray50')+ant("Full sample",2019.1,1.4,4,'gray50')+
geom_segment(x=2017.4,xend=2018.2,y=1,yend=1,color='gray50',linetype="dashed")+ant("High-confidence sample",2019.96,1,4,'gray50'),
width=5,height=4)
# price regression
rgta<-read_csv("Data/public_regression_data.csv")
# see Barbose et al. [4] for further details on the regression variables
library(fixest)
r1<-feols(log(price_w)~loan+ # the loan coefficient is the result of interest reported in the paper
sys_size+sys_size2+ # control variables
prem_module+microinv+dc_optimizer+
mkt_size000+inst_exp000+
hh_sqmi000+median_income000+
new_constr+groundmount+battery|
state+qtr, # fixed effects
data=rgta)
summary(r1)
rh<-feols(log(price_w)~loan+
sys_size+sys_size2+
prem_module+microinv+dc_optimizer+
mkt_size000+inst_exp000+
hh_sqmi000+median_income000+
new_constr+groundmount+battery|
state+qtr,
data=filter(rgta,cl=="H"))
summary(rh)
