Assignment: reproducing Grieco & McDevitt (2017)

Paul Schrimpf 2026-01-12

This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License

The source is available on github.

\[ \def\indep{\perp\!\!\!\perp} \def\Er{\mathrm{E}} \def\R{\mathbb{R}} \def\En{{\mathbb{E}_n}} \def\Pr{\mathrm{P}} \newcommand{\norm}[1]{\left\Vert {#1} \right\Vert} \newcommand{\abs}[1]{\left\vert {#1} \right\vert} \DeclareMathOperator*{\argmax}{arg\,max} \DeclareMathOperator*{\argmin}{arg\,min} \def\inprob{\,{\buildrel p \over \rightarrow}\,} \def\indist{\,{\buildrel d \over \rightarrow}\,} \]

Introduction

This assignment will reproduce some of the results of Grieco and McDevitt (2017).

Installation

Julia

Download and install Julia from https://julialang.org/.

Editor

You will want a good editor for writing code. If you already have a favorite editor, feel free to continue using it. Otherwise, VSCode is a good choice. It is open source, very popular, and supported by a large company. Importantly, this means that it is likely to have continued updates for a long time. Any of the forks of VSCode, such as Cursor (AI focused), Antigravity (Google’s version), or VSCodium (Microsoft free), are also fine. You will also want to install the Julia extension.

Quarto

I recommend using Quarto for writing your solutions, which you should also install. There is also a VSCode extension for Quarto. This document was created using Quarto.

This Assignment

If using Quarto for your answers, download the .qmd file for this assignment from https://github.com/UBCECON567/Dialysis/blob/master/docs/qmd/dialysis-1.qmd , and put it in a folder by itself. For the bibliography to work, you’ll also need https://github.com/UBCECON567/Dialysis/blob/master/docs/qmd/dialysis.bib in the same folder. Change the author in the 4th line of dialysis-1.qmd. You can write your answers below each !!! question block. If you want, you can delete sections that are not questions, but that’s up to you.

If using another tool for your answers, turn in both your code and prose. These could be in the same file (like a Jupyter or Pluto notebook) or separate (e.g. a .jl file of code and pdf of prose). Your code should run, so it should include copies of code blocks from this document that you execute.

Julia resources

This assignment will try to explain aspects of Julia as needed. However, if at some point you feel lost, you may want to consult some of the following resources. Reading the first few sections of either QuantEcon or Think Julia is recommended.

Resources

  • QuantEcon with Julia

  • Think Julia A detailed introduction to Julia and programming more generally. Long, but recommended, especially if you’re new to programming.

  • From the julia prompt, you can access documentation with ?functionname. Some packages have better documentation than others.

  • https://julialang.org/ is the website for Julia

  • Documentation for core Julia can be found at https://docs.julialang.org/en/v1/. All Julia packages also have a github page. Many of these include package specific documentation.

  • The Julia Express short book with examples of Julia usage

Part I: Loading and exploring the data

Loading packages

Like many programming environments (R, Python, etc), Julia relies on packages for lots of its functionality.The following code will download and install all the packages required for this assignment (but the packages will still need to be loaded with using ...). Execute this cell. It will take a few minutes the first time you run it.

using Pkg
Pkg.activate(".")
try
    using Dialysis # This assignment itself is in the "Dialysis" package. We will use some of the functions from it.
catch
    Pkg.develop(PackageSpec(url="https://github.com/UBCECON567/Dialysis"))
    using Dialysis
    Pkg.add(["DataFrames","Statistics","StatsBase","Dates","StatsPlots","Plots"])
end
  Activating project at `~/.julia/dev/Dialysis/docs/qmd`

Load the data

Now let’s get to work. I originally downloaded the data for this problem set from data.cms.gov. Here, you will find zip files for each fiscal year from 2008-2021. As in Grieco and McDevitt (2017) the data comes from Dialysis Facility Reports (DFRs) created under contract to the Centers for Medicare and Medicaid Services (CMS). However, there are some differences. Most notably, this data covers 2004-2019, instead of 2004-2008 as in Grieco and McDevitt (2017).

Question

An aspect of any IO analysis is a comprehensive understanding of industry details and regulations. Important changes in regulations, technology, or industry structure should be taken into account. Search for any changes in the dialysis industry over this time frame. Briefly describe any important changes.

The Julia code in Dialysis/src/data.jl downloads and combines the data files. I did my best to include all the variables (plus more) used by Grieco and McDevitt (2017). However, the underlying data is complicated (there are over 1500 variables each year), so it’s possible I have made mistakes. It might be useful to look at the documentation with any of the “Dialysis Facility Report Data for FY20XX” zip files at data.cms.gov.

The result of the code in data.jl is the dfr.zip file contained in the git repository for this assignment. This zip file contains a csv file with most of the variables used by Grieco and McDevitt (2017), as well as some additional information.

using DataFrames  # DataFrames.jl is a package for storing and
                  # interacting with datasets
using Dialysis # the package for this assignment
dialysis,datadic = loaddata() # loaddata() is a function I wrote that is part
                      # of Dialysis.jl. It returns a Tuple{DataFrame, Dict}
typeof(dialysis)
Reading ZipFile.ReadableFile[ZipFile.ReadableFile(name=dfr.csv, method=Deflate, uncompresssedsize=279942421, compressedsize=89419244, mtime=1.676410132e9)][1]

DataFrame

The variable dialysis now contains a DataFrame with the data. The variable datadic is a Dictionary with brief descriptions of the columns in dialysis.

Use datadic as follows.

datadic["dis2"]
"% hypertension"

For more information on any of the variables, look at the documentation included with any of the “Dialysis Facility Report Data for FY20XX” zip files at data.cms.gov.

We will begin our analysis with some data cleaning. Then we will create some exploratory statistics and figures. There are at least two reasons for this. First, we want to check for any anomalies in the data, which may indicate an error in our code, our understanding of the data, or the data itself. Second, we should try to see if there are any striking patterns in the data that deserve extra attention. We can get some information about all the variables in the data as follows

describe(dialysis)

<div><div style = "float: left;"><span>146×7 DataFrame</span></div><div style = "float: right; font-style: italic;"><span>121 rows omitted</span></div><div style = "clear: both;"></div></div><div class = "data-frame" style = "overflow-x: scroll;">

Rowvariablemeanminmedianmaxnmissingeltype
SymbolUnion…AnyUnion…AnyInt64Type
1provcityABBEVILLEZUNI8Union{Missing, String31}
2owner_fFor ProfitUnavailable0String15
3surveystd_f.900String3
4surveycfc_f.90String3
5modal_f1Unavailable0String
6fiscal_year2015.2320082015.020220Int64
7chainnamAMERI TECH KIDNEY CENTER, INCWHEATON FRANCISCAN SVCS INC WHEATON IL53360Union{Missing, String}
8totstas_f.90String3
9stateAKWY0String3
10provfs100001="852583"0String15
11surveycc_f.Unknown0String31
12network1="18"0String7
13provname- CHEYENNE DIALYSIS (DVA)ZZZ-CLOSED-USRC PMMC LLC0String
135CWpdesarx.98.333295720Union{Missing, String7}
136socwkPT.80String3
137clmalcom.9.929127488Union{Missing, String7}
138clmanem.99.53727488Union{Missing, String7}
139year2011.7320032012.020200Int64
140hty.99.981328504Union{Missing, String7}
141hct33.99.291339212Union{Missing, String7}
142urr65.99.663148764Union{Missing, String7}
143modshd.9.4444296076Union{Missing, String7}
144hctmean.41.1086339212Union{Missing, String7}
145ppfist.99.37569122Union{Missing, String7}
146ppcg90.93.220375742Union{Missing, String7}

</div>

Data Cleaning

From the above, we can see that the data has some problems. It appears that “.” is used to indicate missing. We should replace these with missing. Also, the eltype of most columns is String. We should convert to numeric types where appropriate.

Types

Every variable in Julia has a type, which determines what information the variable can store. You can check the type of a variable with typeof(variable). The columns of our dialysis DataFrame will each be some array-like type that can hold some particular types of elements. For examble, typeof(dialysis[!,:nursePT]) (or equivalently typeof(dialysis.nursePT) should currently be Array{String, 1}. This means that right now the nursePT column can only hold strings. Therefore trying to assign an integer to an element of the column like dialysis[2, :nursePT] = 0 will cause an error. If we want to convert the element type of the column, we have to assign the column to an entirely new array. We will do this below.

Missing

Julia includes a special type and value to represent missing data. The element type of arrays that include missing will be Union{Missing, OTHERTYPE} where OTHERTYPE is something like String or Float64. The Union means each element of the array can hold either type Missing or OTHERTYPE. Some functions will behave reasonably when they encounter missing values, but many do not. As a result, we will have to be slightly careful with how we handle missing values.

Although not apparent in the describe(dialysis) output, it is also worth mentioning the unique format of the data. The data is distributed with one file per fiscal year. Each fiscal year file reports the values of most variables in calendar years 6 to 2 years ago. We need to convert things to have a single calendar year value for each variable.

Type Conversion

We begin by converting types. We will use regular expressions to try identify columns whose strings all look like integers or all look like floating point numbers. Many programming languages have ways to work with regular expressions. It is worth remembering regular expressions are a useful tool for parsing strings, but beyond that do not worry about the dark art of regular expressions too much.

function guesstype(x::AbstractArray{T}) where T <:Union{S,Missing} where S <: AbstractString
    # r"" creates a regular expression
    # regular expressions are useful for matching patterns in strings
    # This regular expression matches strings that are either just "." or begin with - or a digit and are followed by 0 or more additional digits
    #
    # all(array) is true if all elements of the array are true
    #
    # skipmissing(x) creates a iterator over the non-missing elements of x (this iterator will behave like an array of the non-missing elements of x)
    if all(occursin.(r"(^\.$)|(^(-|)\d+)$",skipmissing(x)))
        return Int
    elseif all(occursin.(r"(^\.$)|(^(-|\d)\d*(\.|)\d*$)",skipmissing(x)))
        return Float64
    elseif all(occursin.(
                r"(^\.$)|(^\d\d\D{3}\d{4}$|^\d\d/\d\d/\d{4}$)",
                skipmissing(x)))
        return Date
    else
        return S
    end
end

guesstype(x) = eltype(x)
guesstype (generic function with 2 methods)
Multiple Dispatch

An important Julia feature for organizing code is multiple dispatch. Multiple dispatch refers to having multiple definitions of functions with the same name, and which version of the function gets used is determined by the types of the function arguments. In the code above, we defined a generic guesstype(x) for any type of argument x. We also have a more specialized guesstype(x) function for x that are AbstractArray with element type either String, Missing or Union{String, Missing}. When we call guesstype(whatever) below, the most specific version of guesstype that fits the type of whatever will get used.

guesstype(["1", "3.3", "5"]) # this will call the first definition of guesstype since the argument is an Array of String
Float64
guesstype([12.4, -0.8]) # this will call the second definition of guesstype since the argument is an Array of Float64
Float64

Again using multiple dispatch, we can create a function to convert the types of the columns of the dialysis DataFrame.

Adding Methods to Existing Functions

Base.parse is a function included in Julia for converting strings to numeric types. We want to use parse to convert the types of our DataFrame columns. However, for some columns, we want to leave strings as strings, and for others we want to convert strings to dates.

The builtin parse function only converts strings to numbers. However, we can define additional parse methods and use multiple dispatch to handle these cases.

🏴‍☠️ Adding methods for types that we did not create to existing functions is called type piracy. Type piracy can break code in unexpected ways, so it should be avoided. Instead of definining new methods for Base.parse, we create a new function myparse and add methods to it.🏴‍☠️

This approach will make the converttype function defined below very short and simple.

using Dates
"""
Parse string `x` as type `t`
"""
myparse(t,x) = Base.parse(t,x)
# we need a parse that "converts" a string to string
myparse(::Type{S}, x::S) where S <: AbstractString = x
# a version of parse that works for the date formats in this data
myparse(::Type{Dates.Date}, x::AbstractString) = occursin(r"\D{3}",x) ? Date(x, "dduuuyyyyy") : Date(x,"m/d/y")
myparse (generic function with 3 methods)
converttype(x) = x
function converttype(x::AbstractArray{T}) where T <: Union{Missing, AbstractString}
    etype = guesstype(x)
    return([(ismissing(val) || val==".") ? missing : myparse(etype,val)
        for val in x])
end
converttype (generic function with 2 methods)
Array Comprehension

In converttype, we use an array comprehension to create the return value. Comprehensions are a concise and convenient way to create new arrays from existing ones.

Generator expressions are a related concept.

Now we use apply these functions to the dialysis dataframe.

cleandf = mapcols(converttype, dialysis)
# fix the identifier strings. some years they're listed as ="IDNUMBER", others, they're just IDNUMBER
cleandf.provfs = replace.(cleandf.provfs, "="=>"")
cleandf.provfs = replace.(cleandf.provfs,"\""=>"")
describe(cleandf)

<div><div style = "float: left;"><span>146×7 DataFrame</span></div><div style = "float: right; font-style: italic;"><span>121 rows omitted</span></div><div style = "clear: both;"></div></div><div class = "data-frame" style = "overflow-x: scroll;">

Rowvariablemeanminmedianmaxnmissingeltype
SymbolUnion…AnyAnyAnyInt64Type
1provcityABBEVILLEZUNI8Union{Missing, String31}
2owner_fFor ProfitUnavailable0String15
3surveystd_f5.6932404.015346336Union{Missing, Int64}
4surveycfc_f0.29090900.01246216Union{Missing, Int64}
5modal_f1Unavailable0String
6fiscal_year2015.2320082015.020220Int64
7chainnamAMERI TECH KIDNEY CENTER, INCWHEATON FRANCISCAN SVCS INC WHEATON IL53360Union{Missing, String}
8totstas_f17.4724016.010825352Union{Missing, Int64}
9stateAKWY0String3
10provfs012306933000String
11surveycc_f0Unknown24128Union{Missing, String31}
12network1="18"0String7
13provname- CHEYENNE DIALYSIS (DVA)ZZZ-CLOSED-USRC PMMC LLC0String
135CWpdesarx56.38060.057.7062100.0343444Union{Missing, Float64}
136socwkPT0.53593700.07533963Union{Missing, Int64}
137clmalcom2.451940.01.886857.692381436Union{Missing, Float64}
138clmanem8.435640.05.8824100.081436Union{Missing, Float64}
139year2011.7320032012.020200Int64
140hty46.89870.04140.723318.491333759Union{Missing, Float64}
141hct3390.91190.092.647100.0343008Union{Missing, Float64}
142urr6596.55920.098.0100.0188488Union{Missing, Float64}
143modshd0.01709280.00.085.7143305661Union{Missing, Float64}
144hctmean35.763724.695935.757441.1086343008Union{Missing, Float64}
145ppfist60.00290.061.32100.0111610Union{Missing, Float64}
146ppcg9010.11010.08.657100.0111610Union{Missing, Float64}

</div>

Reshaping

Now, we will deal with the fiscal year/calendar year issues. As mentioned earlier, most variables that vary over time have their values reported for four previous years in each of the fiscal year data files. Thus, for these variables we will have four reports of what should be the same value. The values may not be the same if there are any data entry errors or similar problems. Let’s begin by checking for this.

using Statistics

# the Statistics.var function will give errors with Strings
numcols = (Symbol(c) for c in names(cleandf) if eltype(cleandf[!,c]) <: Union{Missing, Number})
# replace NaN with missing
function variance(x)
    v=var(skipmissing(x))
    return(isnan(v) ? missing : v)
end

# display summary stats of within provfs and year variances
describe(
    # compute variance by provfs and year
    combine(groupby(cleandf, [:provfs, :year]),
            (numcols) .=> variance)
)

<div><div style = "float: left;"><span>137×7 DataFrame</span></div><div style = "float: right; font-style: italic;"><span>112 rows omitted</span></div><div style = "clear: both;"></div></div><div class = "data-frame" style = "overflow-x: scroll;">

Rowvariablemeanminmedianmaxnmissingeltype
SymbolUnion…AnyUnion…AnyInt64Type
1provfs012306933000String
2year2012.320032013.020200Int64
3surveystdfvariance11.56920.00.011100.533464Union{Missing, Float64}
4surveycfcfvariance0.2388690.00.050.033456Union{Missing, Float64}
5fiscalyearvariance1.353270.51.666674.524467Union{Missing, Float64}
6totstasfvariance2.841580.00.01764.028614Union{Missing, Float64}
7clmihdm_variance60.89790.01.025562612.3942581Union{Missing, Float64}
8modcapd_variance0.4689720.00.01477.1239245Union{Missing, Float64}
9canm_variance0.5065190.00.0363.42165256Union{Missing, Float64}
10rac3_variance0.1193090.00.0524.68139245Union{Missing, Float64}
11alcom_variance0.1378020.00.088.844465256Union{Missing, Float64}
12insmdcrom_variance1.539880.00.0424.54165256Union{Missing, Float64}
13clmdrugm_variance1.799560.00.00032154394.5342581Union{Missing, Float64}
126CWpdesarx_variance0.6197380.00.0155.835123761Union{Missing, Float64}
127socwkPT_variance0.001070730.00.02.2533992Union{Missing, Float64}
128clmalcom_variance0.9290130.00.00040004382.52442581Union{Missing, Float64}
129clmanem_variance39.29570.00.0579635000.042581Union{Missing, Float64}
130year_variance0.00.00.00.024467Union{Missing, Float64}
131hty_variance0.2239760.00.0018605661.97121473Union{Missing, Float64}
132hct33_variance130032Missing
133urr65_variance0.9835960.00.02527.1282262Union{Missing, Float64}
134modshd_variance0.06120270.00.0658.435112059Union{Missing, Float64}
135hctmean_variance130032Missing
136ppfist_variance8.004310.00.03599.5960685Union{Missing, Float64}
137ppcg90_variance1.310270.00.02644.6360685Union{Missing, Float64}

</div>

The median variance is generally 0—most providers report variables consistently across years. However, there are large outliers. As a simple, but perhaps not best solution, we will use the median across fiscal years of each variable.

using StatsBase

function combinefiscalyears(x::AbstractArray{T}) where T <: Union{Missing,Number}
    if all(ismissing.(x))
        return(missing)
    else
        v = median(skipmissing(x))
        return(isnan(v) ? missing : v)
    end
end
function combinefiscalyears(x)
    # use most common value
    if all(ismissing.(x))
        return(missing)
    else
        return(maximum(countmap(skipmissing(x))).first)
    end
end

cleandf = combine(groupby(cleandf, [:provfs,:year]),
        names(cleandf) .=> combinefiscalyears .=> names(cleandf))
sort!(cleandf, [:provfs, :year]);

Defining Needed Variables

Now, let’s create the variables we will need.

Labor

The labor related variables all end in FT (for full-time) or PT (for part-time). Create labor as a weighted sum of these variables.

filter(x->occursin.(r"(F|P)T$",x.first), datadic) # list variables ending with PT or FT
Dict{String, String} with 8 entries:
  "socwkFT"  => "social workers full time"
  "ptcarePT" => "patient care technicians part time"
  "dietFT"   => "renal dieticians full time"
  "nursePT"  => "nurses part time"
  "socwkPT"  => "social workers part time"
  "dietPT"   => "renal dieticians part time"
  "nurseFT"  => "nurses full time"
  "ptcareFT" => "patient care technicians full time"
Question

Modify the code below.

cleandf.labor = cleandf[!,:nursePT]*0.5 + cleandf[!,:nurseFT]*1.0 # + more -- you should modify this
130032-element Vector{Union{Missing, Float64}}:
   missing
  0.0
  7.0
  1.5
  6.0
  8.0
   missing
 15.5
 17.0
 17.5
  ⋮
 10.0
  0.0
  4.0
  3.5
  5.0
  5.5
  7.5
  6.0
  8.5

Hiring

We should create hiring for the control function. There is panellag function in Dialysis.jl to help doing so.

Question

Should hiring at time $t$ be $labor_{t} - labor_{t-1}$ or $labor_{t+1}-labor_{t}$? In other words, should it be a backward or forward difference? Check in the paper if needed and modify the code below accordingly.

cleandf.hiring = cleandf.labor - panellag(:labor,cleandf, :provfs, :year, 1)
 # or
#panellag(:labor,cleandf, :provfs, :year, -1) - cleandf.labor ?
130032-element Vector{Union{Missing, Float64}}:
   missing
   missing
  7.0
 -5.5
  4.5
  2.0
   missing
   missing
  1.5
  0.5
  ⋮
  4.0
   missing
  4.0
 -0.5
  1.5
   missing
  2.0
 -1.5
  2.5

Output

There are a few possible output measures in the data.

CMS observes mortality for most, but possibly not all, dialysis patients. To compute mortality rates, the data reports the number of treated patient-years whose mortality will be observed in column dy.

CMS only observes hospitalization for patients whose hospitalization is insured by Medicare. This is a smaller set of patients than those for whom mortality is observed. This number of patient years is in column hdy. (If I recall correctly, the output reported by Grieco and McDevitt (2017) has summary statistics close to hdy).

The column phd report patient-months of hemodialysis. My understanding is that this number/12 should be close to dy. Since there are other forms of dialysis, dy might be larger. On the other hand if there are hemodialysis patients whose mortality is not known, then phd could be larger.

There might also be other reasonable variables to measure output that I have missed.

Question

Choose one (or more) measure of output to use in the tables and figures below and any subsequent analysis.

For-profit and chain indicators

Question

Create a boolean forprofit indicator from the owner_f column, and Fresenius and Davita chain indicators from chainnam

unique(cleandf.owner_f)
3-element Vector{InlineStrings.String15}:
 "Non-profit"
 "For Profit"
 "Unavailable"
countmap(cleandf.chainnam)
Dict{Union{Missing, String}, Int64} with 287 entries:
  "EASTLAND MEMORIAL HOSPITAL DISTRICT"      => 5
  "REGIONAL-IDF"                             => 76
  "BAGBY DIALYSIS, LLC"                      => 6
  "SATELLITE DIALYSIS"                       => 162
  "OHIO HEALTH"                              => 6
  "AMERICAN RENAL ASSOCIATES (ARA)"          => 767
  "RENAL CARE GROUP"                         => 2
  "REGIONAL-ATLANTIC HEALTH SYSTEM"          => 32
  "DAVITA"                                   => 39574
  "BIG HORN BASIN REGIONAL DIALYSIS CTR LL"  => 2
  "DIALYSPA"                                 => 20
  "BIG HORN BASIN REGIONAL DIALYSIS CTR LLC" => 4
  "KRU MEDICAL VENTURES"                     => 5
  "CENTERS FOR DIALYSIS CARE"                => 181
  "GREAT LAKES REGIONAL DIALYSIS"            => 14
  "I DIALYSIS, LLC"                          => 5
  "REGIONAL-SISTERS OF PROVIDENCE"           => 27
  "RPM REALTY, LLC"                          => 5
  "REGIONAL-ANGELO DIALYSIS CENTERS"         => 27
  ⋮                                          => ⋮
cleandf.forprofit = (cleandf.owner_f .== "For Profit") # modify if needed
f(x) = ismissing(x) ? false : occursin(r"(FRESENIUS|FMC)",x) # improve if needed
cleandf.fresenius = f.(cleandf.chainnam)
# Also do something similar for davita and any other chains you think are important
130032-element BitVector:
 0
 0
 0
 0
 0
 0
 0
 0
 0
 0
 ⋮
 0
 1
 1
 1
 1
 0
 0
 0
 0

State Inspection Rates

State inspection rates are a bit more complicated to create.

inspect = combine(groupby(cleandf, :provfs),
                  :surveydt_f => x->[unique(skipmissing(x))])
rename!(inspect, [:provfs, :inspection_dates])
df=innerjoin(cleandf, inspect, on=:provfs)
@assert nrow(df)==nrow(cleandf)
function dayssince(year, dates)
    today = Date(year, 12, 31)
    past = [x.value for x in today .- dates if x.value>=0]
    if length(past)==0
        return(missing)
    else
        return(minimum(past))
    end
end

df=transform(df, [:year, :inspection_dates] => (y,d)->dayssince.(y,d))
rename!(df, names(df)[end] =>:days_since_inspection)
df[!,:inspected_this_year] = ((df[!,:days_since_inspection].>=0) .&
    (df[!,:days_since_inspection].<365))

# then take the mean by state
stateRates = combine(groupby(df, [:state, :year]),
                     :inspected_this_year =>
                         (x->mean(skipmissing(x))) => :state_inspection_rate)
df = innerjoin(df, stateRates, on=[:state, :year])
cleandf=df;

Competitors

Creating the number of competitors in the same city is somewhat similar. Note that Grieco and McDevitt (2017) use the number of competitors in the same HSA, which would be preferrable. However, this dataset does not contain information on HSAs. If you are feeling ambitious, you could try to find data linking city, state to HSA, and use that to calculate competitors in the same HSA.

upcase(x) = Base.uppercase(x)
upcase(m::Missing) = missing
df = cleandf
df[!,:provcity] = upcase.(df[!,:provcity])
comps = combine(groupby(df,[:provcity,:year]),
                :dy =>
                    (x -> length(skipmissing(x).>=0.0)) =>
                    :competitors
                )
comps = comps[.!ismissing.(comps.provcity),:]
df = outerjoin(df, comps, on = [:provcity,:year], matchmissing=:equal)
@assert nrow(df)==nrow(cleandf)
cleandf=df

<div><div style = "float: left;"><span>130032×155 DataFrame</span></div><div style = "float: right; font-style: italic;"><span>55 columns and 130007 rows omitted</span></div><div style = "clear: both;"></div></div><div class = "data-frame" style = "overflow-x: scroll;">

Rowprovfsyearprovcityowner_fsurveystd_fsurveycfc_fmodal_ffiscal_yearchainnamtotstas_fstatesurveycc_fnetworkprovnamesurveydt_fsurveyact_fclmihdmmodcapdcanmrac3alcominsmdcromclmdrugmexdclmcamrac4clminfmnephy6mncmnrshomeclmcdmasianmclmhepothmdrugmshrtCWhdavgHGBmodccpdclmdiabmhtprimclmcanminsnonemsrrpifistptcareFTclmcopdmppcathclmchfmbmifmdis2mefavfmcntcomclmcvdmrac2dbprimageothcarmrdshdis4mefgraftmnephy612mclmpnemhemomdeamodhdpempmtfyeth1vincreamambumcopdmbmimmhdyppavfhtawhitemnephunkmissmmodhhdphdsocwkFTppavgnursePThgmcvamnurseFTstrrCWhdesarxage1blackmsalbmtfdietPTcempminfpvdminsmdcrcdmclmhypthymage3agemclmhivaidm
String?Int64String?String15?Float64?Float64?String?Float64?String?Float64?String3?String31?String7?String?Date?String15?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?Float64?
10123062003BIRMINGHAMNon-profitmissingmissingHemodialysis and Peritoneal Dialysis2008.0missing7.0AL2="08"CHILDRENS HOSPITAL DIALYSIS1995-10-23RECERTIFICATIONmissing0.0missing6.667missingmissingmissing1.4895missing53.333missingmissingmissing0.0missingmissingmissingmissingmissingmissing40.0missingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissing0.0missingmissingmissing40.0missing13.0667missingmissing66.6667missingmissingmissingmissing0.060.0missingmissing0.06.82911missingmissingmissingmissingmissingmissingmissingmissingmissing0.0missingmissingmissingmissingmissingmissingmissingmissingmissing100.0missingmissingmissingmissingmissingmissingmissingmissingmissing0.0missingmissing
20123062004BIRMINGHAMNon-profit5.00.0Hemodialysis and Peritoneal Dialysis2008.5missing7.0AL2="08"CHILDRENS HOSPITAL DIALYSIS2007-09-13RECERTIFICATIONmissing0.0missing0.0missingmissingmissing1.8001missing56.7255missingmissingmissing0.0missingmissingmissingmissing0.9267missing45.906missingmissingmissingmissingmissingmissing0.0missing54.5455missingmissing0.0missingmissingmissing43.2745missing10.6492missing12.078.3625missingmissingmissingmissing0.054.094missingmissing0.04.9589missingmissingmissingmissing8.63445.45517.5missingmissing0.022.01.00.00.0missingmissing0.0missingmissing94.5905missingmissingmissing2.0missingmissingmissingmissingmissing0.0missingmissing
30123062005BIRMINGHAMNon-profit5.00.0Hemodialysis and Peritoneal Dialysis2009.0missing7.0AL2="08"CHILDRENS HOSPITAL DIALYSIS2007-09-13RECERTIFICATIONmissing0.00.00.00.00.0missing1.5866missing58.824missingmissingmissing0.0missing7.143missing0.00.35933missing37.5missing0.0missing0.0missingmissing1.0missingmissingmissingmissing0.0missing0.0missing41.1760.010.88240.013.088.2353missingmissingmissingmissing1.062.5missingmissing0.04.44746.79290.00.0missing7.436missing6.042.857missing0.0missing1.0missing4.010.06150.05.0missingmissing94.11850.03.5missing0.0missingmissing0.00.0missing0.08.6749missing
40123062006BIRMINGHAMNon-profit5.00.0Hemodialysis and Peritoneal Dialysis2009.5missing7.0AL2="08"CHILDRENS HOSPITAL DIALYSIS2007-09-13RECERTIFICATIONmissing0.0missing5.4095missingmissingmissing1.2307missing51.462missingmissingmissing0.0missingmissingmissingmissing1.80802missing68.4211missingmissingmissingmissingmissingmissing3.0missingmissingmissingmissing0.0missingmissingmissing43.1285missing10.8377missing11.089.1815missingmissingmissingmissing2.031.579missingmissing16.2283.37425missingmissingmissingmissing6.385missing25.0missingmissing0.0missing1.0missing1.0missingmissing1.0missingmissing100.0missingmissingmissing0.0missingmissingmissingmissingmissing0.0missingmissing
50123062007BIRMINGHAMNon-profit5.00.012010.0missing7.0AL2="08"CHILDREN'S HOSPITAL DIALYSIS2007-09-13RECERTIFICATIONmissing0.0missing0.0missingmissingmissing1.1839missing33.333missingmissingmissing0.0missingmissingmissingmissing1.42874missing60.0missingmissingmissingmissingmissingmissing0.0missingmissingmissingmissing0.0missingmissingmissing66.667missing13.0667missing13.073.3333missingmissingmissingmissing1.040.0missingmissing6.6673.9082missingmissingmissingmissing6.149missing19.0missingmissing0.0missing1.0missing4.0missingmissing4.0missingmissing100.0missingmissingmissing0.0missingmissingmissingmissingmissing0.0missingmissing
60123062008BIRMINGHAMNon-profit5.00.012010.5missing7.0AL2="08"CHILDREN'S HOSPITAL DIALYSIS2007-09-13RECERTIFICATIONmissing0.00.00.00.00.0missing1.176missing41.176missing27.778missing0.0missing5.556missing0.0missingmissing58.8238missing0.0missing0.0missingmissing0.0missingmissingmissing18.81890.00.00.11111missing58.8240.012.11760.0missing82.3530.05.556missing15.00.029.4120.0missing0.03.50615.32780.00.0missingmissingmissingmissing33.3330.011.7648missing0.0missing10.09.92560.03.0missingmissing94.11861.1113.17778missing1.00.0missing0.00.0missing0.010.9284missing
70123062009BIRMINGHAMNon-profitmissingmissing12011.0missing7.0ALmissing="08"CHILDREN'S HOSPITAL DIALYSISmissingmissingmissing0.0missing0.0missingmissingmissing1.3668missing58.824missingmissingmissing0.0missingmissingmissingmissingmissingmissing47.059missingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissing0.0missingmissingmissing41.176missing12.4118missingmissing82.3529missingmissingmissingmissing1.041.176missingmissing5.8824.3616missingmissingmissingmissingmissingmissingmissingmissingmissing11.765missingmissingmissingmissingmissingmissingmissingmissingmissing82.353missingmissingmissingmissingmissingmissingmissingmissingmissing0.0missingmissing
80123062016BIRMINGHAMNon-profit9.00.0Hemodialysis and Peritoneal Dialysis2021.0INDEPENDENT7.0ALAcceptable Plan of Correction="08"CHILDRENS HOSPITAL OF ALABAMA ESRD2017-01-25Recertification0.00.09.090.00.00.00.01.00250.045.033.333345.45515.00.026.6670.00.00.00.95786missing60.06.6670.06.66670.01.01832missing0.013.3333missing6.667missing0.0missing0.818186.666755.00.08.49.0921.065.0missing0.00.09.00.035.00.0missing10.03.57865.210.00.0missing13.925missing35.054.5450.05.0missing0.0missing3.09.250.014.0missingmissing100.045.4553.27missing0.00.0missing0.00.093.3330.011.34810.0
90123062017BIRMINGHAMNon-profit9.00.0Hemodialysis and Peritoneal Dialysis2021.5INDEPENDENT7.0ALAcceptable Plan of Correction="08"CHILDRENS HOSPITAL OF ALABAMA ESRD2017-01-25Recertification0.00.0missing0.0missingmissing0.01.1529.090947.61963.6364missing11.00.027.2729missing0.0missing1.26276missing47.61913.6364missing4.54545missing1.02841missing0.013.6364missing13.6364missing0.0missingmissing0.052.381missing8.9048missing19.071.4286missingmissing4.54545missing0.047.619missingmissing14.2864.1212missingmissingmissingmissing12.244missing36.0missingmissing4.762missing1.0missing2.0missingmissing16.0missingmissing100.0missingmissingmissing0.0missingmissingmissingmissing40.9090.0missing0.0
100123062018BIRMINGHAMNon-profit9.00.0Hemodialysis and Peritoneal Dialysis2021.5INDEPENDENT7.0ALAcceptable Plan of Correction="08"CHILDRENS HOSPITAL OF ALABAMA ESRD2017-01-25Recertificationmissing0.0missing0.0missingmissingmissing0.8635missing44.444missingmissingmissing0.0missingmissingmissingmissing2.00994missing44.444missingmissingmissingmissing1.73933missing0.0missingmissingmissingmissing0.0missingmissingmissing55.556missing11.8889missing12.055.5556missingmissingmissingmissing5.050.0missingmissing5.5564.9666missingmissingmissingmissing6.412missing22.0missingmissing5.556missing1.0missing1.0missingmissing17.0missingmissing88.889missingmissingmissing0.0missingmissingmissingmissingmissing0.0missingmissing
110123062019BIRMINGHAMNon-profit9.00.0Hemodialysis and Peritoneal Dialysis2021.5INDEPENDENT7.0ALAcceptable Plan of Correction="08"CHILDRENS HOSPITAL OF ALABAMA ESRD2017-01-25Recertificationmissing0.0missing0.0missingmissingmissing0.7missing50.0missingmissingmissing0.0missingmissingmissingmissing0.753185missing50.0missingmissingmissingmissingmissingmissing0.0missingmissingmissingmissing0.0missingmissingmissing50.0missing10.8571missing12.085.7143missingmissingmissingmissing0.042.857missingmissing7.1435.1442missingmissingmissingmissing6.817missing10.0missingmissing7.143missing1.0missing3.0missingmissing12.0missingmissing85.714missingmissingmissing0.0missingmissingmissingmissingmissing0.0missingmissing
120123062020BIRMINGHAMNon-profit9.00.0Hemodialysis and Peritoneal Dialysis2022.0INDEPENDENT7.0ALAcceptable Plan of Correction="08"CHILDRENS HOSPITAL OF ALABAMA ESRD2017-01-25Recertificationmissing0.00.00.00.00.0missing0.453missing45.0missing57.143missing0.0missing0.0missing0.01.27207missing55.0missing0.0missing0.0missingmissing0.0missingmissingmissingmissing0.0missing0.57143missing55.00.09.20.016.080.0missing21.429missing8.00.040.0missingmissing10.04.10734.470.00.0missing5.207missing12.050.00.05.0missing1.0missing2.08.847.1414.0missingmissing95.050.03.37missing0.0missingmissing0.00.0missing0.08.3925missing
130125002003MONTGOMERYFor Profit7.00.0Hemodialysis and Peritoneal Dialysis2008.0FRESENIUS MEDICAL CARE (FMC)34.0AL2="08"FMC CAPITOL CITY2006-04-12RECERTIFICATION27.90719.138.330.00.022.22220.019.6332.325616.52263.953missing86.06.956522.0930.04.6510.00.91159missing10.43556.97713.8899.302311.1111missingmissingmissing16.2791missing44.18628.427921.7391missing2.8888924.418681.73944.44455.75655.56109.014.7826missingmissing4.6512missing21.070.43557.895missing0.874.596149.79170.05.5625.04783.559missing148.022.222missing0.0missingmissingmissingmissing9.6922.78missingmissingmissing0.077.7783.27143missingmissing35.028.571416.6713.88894.65128.695754.5732.326
130021925292014WASHINGTONFor Profit0.00.0Hemodialysis and Peritoneal Dialysis2019.0FRESENIUS MEDICAL CARE15.0DCMeets Requirements5RAI POTOMAC DIALYSIS2014-12-22Initialmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissing0.0missingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissing0.0missing0.0missingmissing0.0missingmissingmissingmissingmissingmissing0.0missingmissingmissingmissingmissingmissingmissingmissing
130022925292015WASHINGTONFor Profit0.00.0Hemodialysis and Peritoneal Dialysis2019.0FRESENIUS MEDICAL CARE15.0DCMeets Requirements5RAI POTOMAC DIALYSIS2014-12-22Initial50.03.63640.00.00.00.05.55563.50350.012.72730.5564.76236.010.90913.8890.016.66670.00.55082missing7.27369.44442.85713.88890.00.4715143.47838.030.55568.980652.777828.34238.18185.2632.016.666787.27342.85757.03644.7640.016.363610.52634.7620.019.00.085.45525.016.9090.04.03286.940.04.7631.62820.92526.456319.014.28690.4761.818412.00.01.6990.08.134.764.01.31491missing0.085.7144.18.01.025.0missing0.038.095263.88925.45559.41318.3333
130023925292016WASHINGTONFor Profit0.00.0Hemodialysis and Peritoneal Dialysis2019.0FRESENIUS MEDICAL CARE15.0DCMeets Requirements5RAI POTOMAC DIALYSIS2014-12-22Initial45.4552.32560.00.00.09.52384.54558.90030.06.97731.81824.76266.011.62815.1520.012.12129.521.12525missing9.30266.66752.38113.63640.01.2529520.09.028.787920.048.48528.11846.511612.51.761921.212191.8633.33358.38370.080.011.62796.254.7629.090916.05.079.0710.042.1050.04.49077.860.00.026.234651.35964.276793.04.76285.7148.1395795.01.09.55971.08.960.03.01.27218missing0.090.4763.5819.00.020.0missing0.023.809584.84834.883760.65256.0606
130024925292017WASHINGTONFor Profit0.00.0Hemodialysis and Peritoneal Dialysis2019.0FRESENIUS MEDICAL CARE15.0DCMeets Requirements5RAI POTOMAC DIALYSIS2014-12-22Initial50.01.0873.850.00.011.53851.470610.4040.04.34829.411811.53868.013.04327.9410.013.23530.00.73156missing7.60970.58842.30813.23530.01.6722112.011.029.411813.580257.35331.199948.91312.02.523.529494.56550.060.30430.0101.08.69574.03.8464.411825.012.082.60933.33351.7481.0874.29726.657.693.8530.316862.1668.724376.019.23180.7697.609972.01.015.74070.08.930.05.01.15957missing0.080.7693.2320.00.033.3330.00.023.076985.29440.217462.864.4118
130025933002014WASHINGTONNon-profit1.00.0Hemodialysis and Peritoneal Dialysis2019.0missing6.0DCMeets Requirements5CHILDREN'S HOSPITAL2016-08-26Recertification9.09090.0missing0.0missingmissing0.01.17330.037.03736.3636missing11.00.09.0909missing0.0missing1.15386missing40.7410.0missing9.0909missing0.94856missing1.09.0909missing18.182missing0.0missingmissing0.048.148missing9.3333missing15.062.963missingmissing0.0missing3.059.259missingmissing29.633.3795missingmissingmissingmissing10.42missing25.0missingmissing0.0missing1.0missing3.0missingmissing4.0missingmissing88.889missingmissingmissing0.0missingmissingmissingmissing90.9090.0missing0.0
130026933002015WASHINGTONNon-profit1.00.0Hemodialysis and Peritoneal Dialysis2019.0missing6.0DCMeets Requirements5CHILDREN'S HOSPITAL2016-08-26Recertificationmissing0.0missing0.0missingmissingmissing1.1182missing55.556missingmissingmissing0.0missingmissingmissingmissing0.69653missing50.0missingmissingmissingmissing0.5086missing0.0missingmissingmissingmissing0.0missingmissingmissing27.778missing8.7222missing14.072.2222missingmissingmissingmissing0.050.0missingmissing38.8893.3missingmissingmissingmissing8.958missing14.0missingmissing0.0missing1.0missing3.0missingmissing6.0missingmissing88.889missingmissingmissing0.0missingmissingmissingmissingmissing0.0missingmissing
130027933002016WASHINGTONNon-profit1.00.0Hemodialysis and Peritoneal Dialysis2019.0missing6.0DCMeets Requirements5CHILDREN'S HOSPITAL2016-08-26Recertificationmissing0.00.00.00.00.0missing0.9138missing55.556missing30.769missing0.0missing15.385missing0.0missingmissing33.333missing0.0missing0.0missing0.00.0missingmissingmissingmissing0.00.00.84615missing33.3330.07.444430.77missing66.66670.00.0missing11.01.061.111missingmissing38.8892.76844.987.690.0missingmissingmissingmissing61.5387.6920.0missing1.0missing2.09.040.05.0missingmissing94.44423.0772.5missing0.0missingmissing0.00.0missing0.08.4776missing
130028933002017WASHINGTONNon-profit1.00.0Hemodialysis and Peritoneal Dialysis2019.0missing6.0DCMeets Requirements5CHILDREN'S HOSPITAL2016-08-26Recertificationmissing0.00.00.00.00.0missing0.874missing50.0missing45.455missing0.0missing9.091missing0.0missingmissing31.25missing0.0missing0.0missingmissing0.0missingmissingmissingmissing0.0missing0.72727missing43.750.08.3750.0missing68.75missing9.091missing10.00.068.75missingmissing37.53.18294.629.090.0missingmissingmissingmissing45.4550.00.0missing1.0missing3.09.570.07.0missingmissing100.045.4553.35missing0.0missingmissing0.00.0missing0.08.8597missing
1300291525192003missingFor ProfitmissingmissingUnavailable2008.0RENAL CARE GROUP INC.6.0IN2="09"RCG - GREENSBURG - CLOSED2001-01-17RECERTIFICATIONmissingmissingmissingmissingmissingmissingmissing1.4382missingmissingmissingmissingmissingmissingmissingmissingmissingmissing1.09649missingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissing17.0missingmissingmissingmissingmissing2.0missingmissingmissingmissingmissingmissingmissingmissingmissing4.282missing9.0missingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissing
1300301525192004missingFor ProfitmissingmissingUnavailable2008.0RENAL CARE GROUP INC.6.0IN2="09"RCG - GREENSBURG - CLOSED2001-01-17RECERTIFICATIONmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissing21.4286missingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissing14.286missingmissingmissingmissing14.0missing21.429missingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissing
1300311525192005missingFor ProfitmissingmissingUnavailable2008.0RENAL CARE GROUP INC.6.0IN2="09"RCG - GREENSBURG - CLOSED2001-01-17RECERTIFICATIONmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissing
1300321525192006missingFor ProfitmissingmissingUnavailable2008.0RENAL CARE GROUP INC.6.0IN2="09"RCG - GREENSBURG - CLOSED2001-01-17RECERTIFICATIONmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissingmissing

</div>

Summary Statistics

Question

Create a table (or multiple tables) similar to Tables 1-3 of Grieco and McDevitt (2017). Comment on any notable differences. The following code will help you get started.

# at the very least, you will need to change this list
vars = [:phd, :labor, :hiring]

# You shouldn't neeed to change this function, but you can if you want
function summaryTable(df, vars;
                      funcs=[mean, std, x->length(collect(x))],
                      colnames=[:Variable, :Mean, :StdDev, :N])
    # In case you want to search for information about the syntax used here,
    # [XXX for XXX] is called a comprehension
    # The ... is called the splat operator
    DataFrame([vars [[f(skipmissing(df[!,v])) for v in vars] for f in funcs]...], colnames)
end
summaryTable(cleandf, vars)

<div><div style = "float: left;"><span>3×4 DataFrame</span></div><div style = "clear: both;"></div></div><div class = "data-frame" style = "overflow-x: scroll;">

RowVariableMeanStdDevN
AnyAnyAnyAny
1phd715.115514.156102921
2labor5.29574.48049111106
3hiring0.07368522.62668100451

</div>

Figures

Question

Create some figures to explore the data. Try to be creative. Are there any strange patterns or other obvious problems with the data?

Here are some examples to get started. You may want to look at the StatPlots.jl, Plots.jl, VegaLite.jl, or AlgebraOfGraphics.jl github pages for more examples.

using StatsPlots
vars = [:dy, :hdy, :phd]
inc = completecases(cleandf[!,vars]) # missings will mess up corrplot
@df cleandf[inc,vars] corrplot(cols(vars))

function yearPlot(var; df=cleandf)
  data = df[completecases(df[!,[:year, var]]),:]
  scatter(data[!,:year], data[!,var], alpha=0.1, legend=:none,
          markersize=3, markerstrokewidth=0.0)
    q = [0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99]
  yearmeans = combine(groupby(data, :year),
               var => (x->[(mean(skipmissing(x)),
                          quantile(skipmissing(x),q)...)])  =>
        ["mean", (x->"q$(Int(100*x))").(q)...])
    sort!(yearmeans,:year)
  @df yearmeans plot!(:year, :mean, colour = ^(:black), linewidth=4)
  fig = @df yearmeans plot!(:year, cols(3:ncol(yearmeans)),
                      colour = ^(:red), alpha=0.4, legend=:none,
                      xlabel="year", ylabel=String(var))
  return(fig)
end
fig=yearPlot(:labor)
plot!(fig, ylim=[0,50]) # adjust y-axis range

The above plot shows a scatter of labor vs year. The black lines are average labor each year. The red lines are the 0.01, 0.1, 0.25, 0.5, 0.75, 0.9, and 0.99 quantiles conditional on year.

Question

Please hand in both your modified dialysis-1.qmd and an html or pdf rendering of it. To render html, execute quarto render dialysis-1.qmd

<div id="refs" class="references csl-bib-body hanging-indent" entry-spacing="0">

<div id="ref-grieco2017" class="csl-entry">

Grieco, Paul L. E., and Ryan C. McDevitt. 2017. “Productivity and Quality in Health Care: Evidence from the Dialysis Industry.” The Review of Economic Studies 84 (3): 1071–1105. https://doi.org/10.1093/restud/rdw042.

</div>

</div>