proptax 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,161 @@
1
+ # Proptax
2
+
3
+ Process property assessment reports provided by the City of Calgary and automatically report and visualize on discrepencies in the data.
4
+
5
+ I currently have three victories before Calgary's [Assessment Review Board](http://www.calgaryarb.ca/eCourtPublic/). Go to [TaxReformYYC](https://taxreformyyc.com) for more information.
6
+
7
+ This software automatically generates the reports I submit as evidence before the ARB.
8
+
9
+ # Setup
10
+
11
+ `proptax` is a command line `ruby` program developed under Ubuntu 16.04. It is free to use and entirely open source, so it is probably deployable on MacOS and maybe Windows with some massaging. If you figure it out, please document the process and submit a pull request. I will gladly add your contribution to this software.
12
+
13
+ ## Dependencies
14
+
15
+ `proptax` combines and coordinates the output of multiple open source resources. In broad terms, it requires the following packages to generate the reports:
16
+
17
+ 1. `gs`
18
+ 2. `tesseract`
19
+ 3. `enscript`
20
+ 4. `pandoc`
21
+ 5. `R`
22
+
23
+ The following commands will install all third party dependencies:
24
+
25
+ ```
26
+ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
27
+ sudo add-apt-repository 'deb [arch=amd64,i386] https://cran.rstudio.com/bin/linux/ubuntu xenial/'
28
+ sudo apt update
29
+ sudo apt install -y ghostscript tesseract-ocr enscript pandoc r-base r-base-dev r-cran-scales libmagick++-dev mesa-common-dev libglu1-mesa-dev texlive-fonts-recommended texlive-latex-recommended
30
+ ```
31
+
32
+ ### R
33
+
34
+ `R` does the bulk of the data processing. It has some dependencies that are not available from Ubuntu 16.04 (Xenial) PPAs. They need to be installed into the `R` environment directly.
35
+
36
+ Execute the following to open the `R` command prompt:
37
+
38
+ ```
39
+ R
40
+ ```
41
+
42
+ Then, at the `>` prompt, execute the following `R` commands:
43
+
44
+ ```
45
+ install.packages('knitr', dependencies = TRUE)
46
+ install.packages('scales', dependencies = TRUE)
47
+ install.packages('formattable', dependencies = TRUE)
48
+ install.packages('ggplot2', dependencies = TRUE)
49
+ ```
50
+
51
+ Assuming successful installation, you can exit `R` by holding `Ctrl-D`.
52
+
53
+ ## Install proptax
54
+
55
+ `proptax` is a `ruby` program. As such, you need to [install ruby](https://www.digitalocean.com/community/tutorials/how-to-install-ruby-on-rails-with-rbenv-on-ubuntu-16-04).
56
+
57
+ You install the latest release of `proptax` like this:
58
+
59
+ ```
60
+ gem install proptax
61
+ ```
62
+
63
+ # Usage
64
+
65
+ `proptax` reports-on and visualizes the data contained in residential property reports provided by the City of Calgary. Your property report and those of your neighbours can be obtained at [assessmentsearch.calgary.ca](https://assessmentsearch.calgary.ca).
66
+
67
+ Last year I made a whole series of super-boring [YouTube tutorials](https://www.youtube.com/playlist?list=PLkQAXLFkBnmiB8O06C2oGAoarBCVO7M9J) on how to collect and process your property data. The collection process has changed slightly, but [the first video](https://www.youtube.com/watch?v=m0zzsL0DYlI&list=PLkQAXLFkBnmiB8O06C2oGAoarBCVO7M9J&index=2) should point you in the right direction. You only get 50 reports per year for some reason, so use 'em all up (and send them to me)!
68
+
69
+ TODO: More usage instructions to come...
70
+
71
+ # Development
72
+
73
+ Install third-party software as with _Setup > Dependencies_, above.
74
+
75
+ Clone this repository:
76
+
77
+ ```
78
+ git clone https://github.com/TaxReformYYC/report-generator-2018.git
79
+ ```
80
+
81
+ Install `ruby` dependencies:
82
+
83
+ ```
84
+ cd report-generator-2018
85
+ bin/setup
86
+ ```
87
+
88
+ ### Test:
89
+
90
+ ```
91
+ bundle exec rake spec
92
+ ```
93
+
94
+ ### To execute the `proptax` script within the development environment:
95
+
96
+ ```
97
+ bundle exec exe/proptax
98
+ ```
99
+
100
+ ### To build the gem:
101
+
102
+ ```
103
+ bundle exec rake build
104
+ ```
105
+
106
+ The package will be found in the `pkg/` directory.
107
+
108
+ ### To install this gem onto your local machine, run:
109
+
110
+ ```
111
+ bundle exec rake install
112
+ ```
113
+
114
+ If that doesn't work, try this:
115
+
116
+ ```
117
+ gem install pkg/proptax-0.1.0.gem # Note version number
118
+ ```
119
+
120
+ Execute the program:
121
+
122
+ ```
123
+ proptax
124
+ ```
125
+
126
+ If installed correctly, you will see help instructions.
127
+
128
+ ### To release a new version:
129
+
130
+ Update the version number in `version.rb`, and then run:
131
+
132
+ ```
133
+ bundle exec rake release
134
+ ```
135
+
136
+ This will create a `git` tag for the version, push `git` commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org):
137
+
138
+ # Contributing
139
+
140
+ Bug reports and pull requests are welcome.
141
+
142
+ ## TODOs:
143
+
144
+ - Speed up tests. Remove setup redundancies
145
+ - Deploy auto CHANGELOG
146
+ - DRY out `R` code
147
+ - Deploy `tesseract` OCR on rasterized PDFs (as with Windows 7).
148
+ - Custom report template documentation
149
+ - Auto-install gem's third-party dependencies
150
+ - Set up wiki for use on different operating systems
151
+ - Dependencies require X11. It would be nice to run this on an Ubuntu 16.04 Server somehow
152
+
153
+ Suggestions? Contribute or [donate](https://taxreformyyc.com/donate)!
154
+
155
+ ## Future:
156
+
157
+ - Basic API for property data submission, collection, and retrieval
158
+
159
+ # Licence
160
+
161
+ GNU General Public License v3.0
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "proptax"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/proptax ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'proptax/cli'
3
+ Proptax::CLI.start
@@ -0,0 +1,68 @@
1
+
2
+ # How do this factors impact an assessment?
3
+ unknownImpact <- c('Taxation.Status', 'Assessment.Class',
4
+ 'Property.Type', 'Property.Use', 'Valuation.Approach',
5
+ 'Market.Adjustment', 'Community', 'Market.Area', 'Sub.Neighbourhood.Code..SNC.',
6
+ 'Sub.Market.Area', 'Influences', 'Land.Use.Designation', 'Building.Count',
7
+ 'Building.Type.Structure', 'Year.of.Construction', 'Quality', 'Basement.Suite',
8
+ 'Walkout.Basement', 'Garage.Type', 'Fireplace.Count',
9
+ 'Constructed.On.Original.Foundation', 'Modified.For.Disabled',
10
+ 'Old.House.On.New.Foundation', 'Basementless', 'Penthouse')
11
+
12
+
13
+ # These factors don't directly affect the assessment
14
+ noImpact <- c('Roll.Number', 'Location.Address')
15
+
16
+ # Load data
17
+ csvFile <- commandArgs(trailingOnly = TRUE)
18
+ data <- read.csv(csvFile, header=TRUE)
19
+
20
+ # Identify common factors
21
+ headers <- c()
22
+ identical <- c()
23
+ for (col in colnames(data)) {
24
+ values = data[,col][!duplicated(data[,col])]
25
+ if (length(values) == 1) {
26
+ headers <- append(headers, col)
27
+ identical <- append(identical, toString(values))
28
+ }
29
+ }
30
+ commonFactors <- data.frame(headers, identical)
31
+
32
+ # Remove common factors
33
+ data <- data[,!(names(data) %in% commonFactors$headers)]
34
+
35
+ # Identify unknown factors
36
+ unknownFactors <- names(data[,names(data) %in% unknownImpact])
37
+
38
+ # Remove unknown factors from data set
39
+ data <- data[,!(names(data) %in% unknownFactors)]
40
+
41
+ # Remove irrelevant factors from data set, but preserve address for plot labels
42
+ rowNames <- data[,noImpact[2]]
43
+ data <- data[,!(names(data) %in% noImpact)]
44
+
45
+ # Label assessment records
46
+ rownames(data) <- rowNames
47
+
48
+ # Sum each property's lot size and total developed space
49
+ areaTotals <- rowSums(data[,-1])
50
+
51
+ # Isolate all assessed values
52
+ assessedValues <- data[,1]
53
+
54
+ # Remove the street names from the addresses
55
+ houseNumbers <- as.numeric(gsub("[^\\d]+", "", rowNames, perl=TRUE))
56
+
57
+ # Plot the best fit regression line
58
+ reg <- lm(assessedValues~areaTotals)
59
+
60
+ # Plot distances between points and the regression line
61
+ assessedDifferences <- residuals(reg)
62
+ adjustedValues <- predict(reg)
63
+
64
+ # Reconcile adjusted property values
65
+ discrepancies <- round(assessedDifferences/assessedValues*100, 2)
66
+ adjustedProperties <- data.frame(houseNumbers, assessedValues, adjustedValues, assessedDifferences, discrepancies)
67
+
68
+ print(adjustedProperties)
@@ -0,0 +1,89 @@
1
+ require 'thor'
2
+ require 'proptax'
3
+ require 'proptax/generators/report'
4
+ module Proptax
5
+ class CLI < Thor
6
+ check_unknown_options!
7
+
8
+ # 2016-2-29 http://stackoverflow.com/questions/14346285/how-to-make-two-thor-tasks-share-options
9
+ shared_options = [:ylimit, {
10
+ :type => :string,
11
+ :default => "10000",
12
+ :description => "Expand y-axis limits"}]
13
+ report_options = [:template, {
14
+ :type => :string,
15
+ :default => "default",
16
+ :description => "Apply specific template: [default, cherry-picked]"}]
17
+ # consolidate_options = [:consolidate, {
18
+ # :type => :boolean,
19
+ # :default => true,
20
+ # :description => "Consolidate the PDFs before generating the report"}]
21
+
22
+ desc "consolidate DIR", "Outputs CSV data extracted from 2018 property assessment reports"
23
+ def consolidate(dir)
24
+ Proptax::Consolidator.process(dir)
25
+ end
26
+
27
+ desc "reports CSV_FILE", "Generate assessment reports"
28
+ method_option *shared_options
29
+ method_option *report_options
30
+ # method_option *consolidate_options
31
+ def reports(dir)
32
+ csv_file = 'consolidated.csv'
33
+ if options.consolidate?
34
+ `proptax consolidate #{dir} > #{csv_file}`
35
+ else
36
+ csv_file = "#{dir}/consolidated.csv"
37
+ end
38
+ Proptax::Generators::Report.start([csv_file, options])
39
+ generate_material("reports")
40
+ end
41
+
42
+ desc "auto DIR", "Automatically create CSV file and reports"
43
+ method_option *shared_options
44
+ method_option *report_options
45
+ def auto(dir)
46
+ `proptax consolidate #{dir} > consolidated.csv`
47
+ Proptax::Generators::Report.start(['consolidated.csv', options])
48
+ generate_material("reports")
49
+ end
50
+
51
+ desc "filter CSV_FILE", "Calculate and display assessment discrepancies"
52
+ method_option :csv,
53
+ :type => :boolean,
54
+ :default => false,
55
+ :description => "Output in CSV format"
56
+ method_option :header,
57
+ :type => :boolean,
58
+ :default => true,
59
+ :description => "Include header row in CSV format"
60
+ def filter(csv)
61
+ data_frame = `Rscript "#{__dir__}"/../R/filter_csv.R "#{csv}"`
62
+ if options[:csv]
63
+ lines = data_frame.split("\n")
64
+ # Print header
65
+ puts lines[0].squeeze(' ').split(' ').to_csv if options[:header]
66
+
67
+ # Print data (minus R-inserted integer row name)
68
+ lines[1..-1].each do |line|
69
+ puts line.squeeze(' ').split(' ')[1..-1].to_csv
70
+ end
71
+ else
72
+ puts data_frame
73
+ end
74
+ end
75
+
76
+ no_commands do
77
+ def generate_material(dir)
78
+ Dir.foreach(dir) do |file|
79
+ if /\.Rmd/.match(file)
80
+ puts "#{file}"
81
+ `cd "#{dir}" && Rscript -e "library('knitr'); knit('#{file}');"`
82
+ file.gsub!('.Rmd', '.md')
83
+ `cd "#{dir}" && Rscript -e "library('knitr'); pandoc('#{file}', format = 'latex');"`
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,296 @@
1
+ ---
2
+ title: 2017 Property Assessment Analysis
3
+ author: Prepared by reports@taxreformyyc.com
4
+ geometry: margin=1.5cm
5
+ ---
6
+
7
+ ```{r loadLibraries, echo=FALSE, message=FALSE}
8
+ # Load the required libraries
9
+ library(knitr)
10
+ library(scales)
11
+ library(formattable)
12
+ library(ggplot2)
13
+ ```
14
+
15
+ ```{r defineConstants, echo=FALSE}
16
+ # Constants
17
+ address <- "<%= address %>"
18
+ myAssessedValue <- <%= assessed_value %>
19
+ csvFile <- "<%= csv_file %>"
20
+
21
+ # Get the house number and street name
22
+ #myHouseNumber <- gsub("[^\\d]+", "", address, perl=TRUE)
23
+ m <- regexpr("^\\d+", address, perl=TRUE)
24
+ myHouseNumber <- regmatches(address, m)
25
+ myStreetName <- gsub(".*[\\d]", "", address, perl=TRUE)
26
+
27
+ # How do this factors impact an assessment?
28
+ unknownImpact <- c('Valuation.Approach', 'Assessment.Class',
29
+ 'Property.Type', 'Property.Use', 'Taxation.Status',
30
+ 'Community', 'Sub.Neighbourhood.Code..SNC.', 'Market.Area',
31
+ 'Sub.Market.Area', 'Land.Use.Designation',
32
+ 'Building.Count', 'Building.Type.Structure',
33
+ 'Year.of.Construction', 'Quality', 'Basement.Suite',
34
+ 'Walkout.Basement', 'Garage.Type', 'Fireplace',
35
+ 'Influences', 'Market.Adjustment')
36
+
37
+ # These factors are informational and don't affect the assessment
38
+ noImpact <- c('Roll.Number', 'Location.Address')
39
+ ```
40
+
41
+ ```{r loadData, echo=FALSE}
42
+ # Load data
43
+ data <- read.csv(csvFile, header=TRUE)
44
+ ```
45
+
46
+ ```{r getMetaData, echo=FALSE}
47
+ streetAddresses <- data[,noImpact[2]]
48
+
49
+ # Remove the street names from the addresses
50
+ #houseNumbers <- as.numeric(gsub("[^\\d]+", "", streetAddresses, perl=TRUE))
51
+ m <- regexpr("^\\d+", streetAddresses, perl=TRUE)
52
+ houseNumbers <- as.numeric(regmatches(streetAddresses, m))
53
+
54
+ ```
55
+
56
+ \center
57
+ `r address`
58
+
59
+ \flushleft
60
+
61
+ # Synopsis
62
+
63
+ This analysis pertains to the property located at **`r address`**.
64
+ It documents the treatment of the assessment data provided by the City of
65
+ Calgary for the pertinent property and those deemed comparable:
66
+
67
+ `r kable(data[order(data$Location.Address),]$Location.Address,
68
+ col.names=c('Comparable Properties'), align=c('l'))`
69
+
70
+ The data under investigation was obtained from
71
+ [assessmentsearch.calgary.ca](https://assessmentsearch.calgary.ca) and
72
+ is presented alongside this document in a consolidated CSV file.
73
+
74
+ # Approach Overview
75
+
76
+ The data investigated in this analysis consists of properties chosen for their
77
+ similar features and proximity to one another. The number of properties chosen
78
+ was maximized to ensure a fair representation of valuations and to support the
79
+ integrity of this report and its conclusion.
80
+
81
+ Given that there are many factors contained in the data whose precise impact on
82
+ assessed values are unknown, commonalities are identified and omitted from
83
+ consideration, as all such factors should have identical impact on the
84
+ final assessment.
85
+
86
+ Factors that vary are identified and presented for consideration, as
87
+ transparency and integrity is of the utmost importance. Again, the precise
88
+ impact of these factors on the final assessment is unknown, as the weights
89
+ assigned by the City of Calgary are not divulged in the assessment data they
90
+ provide.
91
+
92
+ Having acknowledged the factors that cannot easily be quantified, the focus
93
+ turns to the properties' lot sizes, total developed area, and assessed values.
94
+ By visualizing the relationship between these quantifiable factors, the
95
+ pertinent property's assessed value is contrasted with those of the selected
96
+ properties. The conclusion of this analysis is drawn from the underlying data.
97
+
98
+ # Identify common factors
99
+
100
+ Many of the factors that impact the assessments are identical. This data can
101
+ safely be removed from consideration, as the impact on the assessed values
102
+ should be the same for all the properties under investigation.
103
+
104
+ ```{r commonFactors, echo=FALSE}
105
+ # Identify common factors
106
+ headers <- c()
107
+ identical <- c()
108
+ displayHeaders <- c()
109
+ for (col in colnames(data)) {
110
+ values = data[,col][!duplicated(data[,col])]
111
+ if (length(values) == 1) {
112
+ headers <- append(headers, col)
113
+ displayHeaders <-append(displayHeaders, gsub("\\.", " ", col, perl=TRUE))
114
+ identical <- append(identical, toString(values))
115
+ }
116
+ }
117
+ commonFactors <- data.frame(headers, displayHeaders, identical)
118
+ ```
119
+
120
+ Here, **`r length(commonFactors$headers)`** common factors can safely be
121
+ removed from the data set:
122
+
123
+ `r kable(data.frame(commonFactors$displayHeaders, commonFactors$identical),
124
+ col.names=c('Factors', 'Identical Values'), align=c('l', 'r'))`
125
+
126
+ ```{r removeCommonFactors, echo=FALSE}
127
+ # Remove common factors
128
+ data <- data[,!(names(data) %in% commonFactors$headers)]
129
+ ```
130
+
131
+
132
+ # Identify unknown and non-impacting factors
133
+
134
+ Of the **`r length(colnames(data))`** remaining columns, some cannot be
135
+ quantified. Others certainly impact the assessed value of a property, but the
136
+ assessment data provided by the City of Calgary does not reveal to what extent.
137
+
138
+ ## Non-impacting factors
139
+
140
+ ```{r noImpactDisplayHeaders, echo=FALSE}
141
+ # Remove the dots from the header name
142
+ noImpactDisplayHeaders <- gsub("\\.", " ", noImpact, perl=TRUE)
143
+ ```
144
+
145
+ These factors cannot be quantified and are administrative in purpose:
146
+
147
+ `r kable(noImpactDisplayHeaders, col.names=c('Non-Impacting Factors'))`
148
+
149
+ ```{r removeIrrelevantFactors, echo=FALSE}
150
+ data <- data[,!(names(data) %in% noImpact)]
151
+ ```
152
+
153
+ These are removed and the remaining **`r length(colnames(data))`** columns are
154
+ carried forward.
155
+
156
+ ## Unknown factors
157
+
158
+ The impact these remaining columns have on assessment values is unknown:
159
+
160
+ ```{r identifyUnknowns, echo=FALSE}
161
+ # Identify unknown factors
162
+ unknownFactors <- names(data[,names(data) %in% unknownImpact])
163
+ ```
164
+
165
+ `r kable(gsub("\\.", " ", unknownFactors), col.names=c('Unknown Factors'))`
166
+
167
+ The variability within these unknown columns is presented here in the interest
168
+ of transparency:
169
+
170
+ ```{r consolidateUnknownFactors, echo=FALSE}
171
+ consolidatedUnknownFactors <- data[,(names(data) %in% c(unknownFactors))]
172
+ rownames(consolidatedUnknownFactors) <- houseNumbers
173
+ ```
174
+
175
+ `r kable(consolidatedUnknownFactors[order(as.numeric(row.names(consolidatedUnknownFactors))),],
176
+ align=c(rep('c', length(unknownFactors))),
177
+ row.names=TRUE,
178
+ col.names=gsub("\\.", " ", unknownFactors))`
179
+
180
+ These undoubtedly have an impact on the valuation, but their precise weighting
181
+ and significance are not presented in the assessment data provided by the City
182
+ of Calgary. As such, they are removed from the dataset.
183
+
184
+ ```{r removeUnknownFactors, echo=FALSE}
185
+ # Remove unknown factors from data set
186
+ data <- data[,!(names(data) %in% unknownFactors)]
187
+ ```
188
+
189
+ The remaining **`r length(colnames(data))`** columns contain the following data:
190
+
191
+ ```{r addRowNamesToData, echo=FALSE}
192
+ # Add row names to data
193
+ rownames(data) <- houseNumbers
194
+ ```
195
+
196
+ `r kable(data[order(as.numeric(row.names(data))),], row.names=TRUE, col.names=gsub("\\.", " ", colnames(data)))`
197
+
198
+ # Visualization
199
+
200
+ The raw data presented above is summarized in Figure 1. It
201
+ illustrates the disparity between the assessed property values. The pertinent
202
+ property is coloured red.
203
+
204
+ The blue line running through the graph is _best fit_ for the visualized model.
205
+ It serves as a predictor, or indicator, as to where the properties in question
206
+ should be positioned.
207
+
208
+ The pertinent property's overassessment is determined by measuring the distance
209
+ between the red point and the blue line.
210
+
211
+
212
+ ```{r adjustValues, echo=FALSE}
213
+ # Sum each property's lot size and total developed space
214
+ areaTotals <- rowSums(data[,-1])
215
+
216
+ # Isolate all assessed values
217
+ assessedValues <- data[,1]
218
+
219
+ # Plot the best fit regression line
220
+ reg <- lm(assessedValues~areaTotals)
221
+
222
+ # Plot distances between points and the regression line
223
+ assessedDifferences <- residuals(reg)
224
+ adjustedValues <- predict(reg)
225
+
226
+ # Reconcile adjusted property values
227
+ adjustedProperties <- data.frame(houseNumbers, adjustedValues, assessedDifferences)
228
+ ```
229
+
230
+ ```{r generatePlot, fig.cap=paste(address, "Overassessment", " "), fig.width=12, echo=FALSE}
231
+ plot.title <- paste(address, "and Comparable Properties", sep=" ")
232
+ plot.subtitle = 'Current Assessed Property Values'
233
+ ggplot(data, aes(x=areaTotals, y=assessedValues)) +
234
+ # Plot title
235
+ ggtitle(bquote(atop(bold(.(plot.title)), atop(italic(.(plot.subtitle)), "")))) +
236
+ theme(plot.title=element_text(size=24, hjust = 0.5)) +
237
+
238
+ # Axis labels
239
+ ylab("Assessed House Values on your Street") +
240
+ xlab("Total House and Lot Size (Sq. Feet)") +
241
+ theme(axis.title.x=element_text(size=18, face="bold", margin=margin(20,0,0,0)),
242
+ axis.title.y=element_text(size=18, face="bold", margin=margin(0,20,0,0))) +
243
+
244
+ # Axis tick labels
245
+ scale_x_continuous(labels=comma) +
246
+ <% if opts.ylimit? %>
247
+ scale_y_continuous(labels=dollar, breaks=pretty_breaks(n=10),
248
+ limits=c(min(assessedValues)-<%= opts.ylimit %>, max(assessedValues)+<%= opts.ylimit %>)) +
249
+ <% else %>
250
+ scale_y_continuous(labels=dollar, breaks=pretty_breaks(n=10)) +
251
+ <% end %>
252
+ # Scatter plot points
253
+ geom_point(shape=ifelse(assessedValues==myAssessedValue, 16, 1),
254
+ size=ifelse(assessedValues==myAssessedValue, 5, 4),
255
+ colour=ifelse(assessedValues==myAssessedValue, "red", "blue")) +
256
+
257
+ # Point labels
258
+ geom_text(aes(label=houseNumbers), hjust=0.5, vjust=-2, size=5) +
259
+ geom_text(aes(label=paste("$", accounting(assessedValues, format="d"), sep="")),
260
+ hjust=0.5, vjust=-1.2) +
261
+
262
+ # Best fit line
263
+ geom_smooth(method=lm)
264
+ ```
265
+
266
+ # Conclusion
267
+
268
+ The data investigated in this analysis describes the factors considered in
269
+ assessing the property located at `r address`.
270
+ It was collected and provided by the City of Calgary. This report set out to
271
+ quantify the disparity between the pertinent property and similar properties
272
+ in the the neighbourhood.
273
+
274
+ Common factors were identified and eliminated from the analysis. Similarly,
275
+ varying factors were identified, catalogued, and removed from consideration. The
276
+ City of Calgary's property reports do not describe how these characteristic
277
+ features are quantified and weighted in determining a property's assessed
278
+ value. As such, they could not be included.
279
+
280
+ The conclusions that follow are drawn from comparing lot sizes, total developed
281
+ area, and assessed values. The underlying data and the overall approach have
282
+ been presented in full.
283
+
284
+ ## Overvaluation: `r currency(adjustedProperties[houseNumbers==myHouseNumber,]$assessedDifferences)`
285
+
286
+ This investigation compared the assessed values with lot sizes and developed
287
+ square footage. It has revealed that that the pertinent property is overvalued
288
+ by
289
+ **`r currency(adjustedProperties[houseNumbers==myHouseNumber,]$assessedDifferences)`**.
290
+
291
+ ## Corrected Assessed Value: `r currency(adjustedProperties[houseNumbers==myHouseNumber,]$adjustedValues)`
292
+
293
+ In order to bring the pertinent property's assessed value in line with those of
294
+ similar properties in the neighbourhood, it must be reassessed at
295
+ **`r currency(adjustedProperties[houseNumbers==myHouseNumber,]$adjustedValues)`**.
296
+