@dra2020/district-analytics 6.3.0 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +126 -245
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +11 -11
- package/dist/district-analytics.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -89099,10 +89099,10 @@ exports.scorePolsbyPopper = scorePolsbyPopper;
|
|
|
89099
89099
|
/*!*************************!*\
|
|
89100
89100
|
!*** ./src/config.json ***!
|
|
89101
89101
|
\*************************/
|
|
89102
|
-
/*! exports provided: partisan, minority,
|
|
89102
|
+
/*! exports provided: partisan, minority, compactness, splitting, popdev, default */
|
|
89103
89103
|
/***/ (function(module) {
|
|
89104
89104
|
|
|
89105
|
-
module.exports = JSON.parse("{\"partisan\":{\"bias\":{\"range\":[0,0.2],\"weight\":[50,80]},\"impact\":{\"weight\":[50,0],\"threshold\":4},\"competitiveness\":{\"range\":[0,0.75],\"distribution\":[0.25,0.75],\"simpleRange\":[0.45,0.55],\"weight\":[0,20]},\"bonus\":2
|
|
89105
|
+
module.exports = JSON.parse("{\"partisan\":{\"bias\":{\"range\":[0,0.2],\"weight\":[50,80]},\"impact\":{\"weight\":[50,0],\"threshold\":4},\"competitiveness\":{\"range\":[0,0.75],\"distribution\":[0.25,0.75],\"simpleRange\":[0.45,0.55],\"weight\":[0,20]},\"bonus\":2},\"minority\":{\"range\":[0.37,0.5],\"distribution\":[0.25,0.75],\"shift\":0.15,\"coalition\":{\"weight\":0.5}},\"compactness\":{\"reock\":{\"range\":[0.25,0.5],\"weight\":50},\"polsby\":{\"range\":[0.1,0.5],\"weight\":50}},\"splitting\":{\"county\":{\"range\":[[1.26,1.68],[1.09,1.45]],\"weight\":50},\"district\":{\"range\":[[1.26,1.68],[1.09,1.45]],\"weight\":50}},\"popdev\":{\"range\":[[0.0075,0.002],[0.1,-1]]}}");
|
|
89106
89106
|
|
|
89107
89107
|
/***/ }),
|
|
89108
89108
|
|
|
@@ -89118,7 +89118,8 @@ module.exports = JSON.parse("{\"partisan\":{\"bias\":{\"range\":[0,0.2],\"weight
|
|
|
89118
89118
|
//
|
|
89119
89119
|
// THRESHOLDS, SCALES, AND WEIGHTS FOR SCORING MODEL
|
|
89120
89120
|
//
|
|
89121
|
-
// * A layer of functions over config.json, so that they
|
|
89121
|
+
// * A layer of functions over config.json, so that they could be overridden dynamically.
|
|
89122
|
+
// That is not implemented yet.
|
|
89122
89123
|
//
|
|
89123
89124
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
89124
89125
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -89160,7 +89161,7 @@ function competitivenessWeight(context, overridesJSON) {
|
|
|
89160
89161
|
return cW;
|
|
89161
89162
|
}
|
|
89162
89163
|
exports.competitivenessWeight = competitivenessWeight;
|
|
89163
|
-
//
|
|
89164
|
+
// The simple user-facing range, i.e., 45–55%.
|
|
89164
89165
|
function competitiveRange(overridesJSON) {
|
|
89165
89166
|
const range = config_json_1.default.partisan.competitiveness.simpleRange;
|
|
89166
89167
|
return range;
|
|
@@ -89171,9 +89172,9 @@ function competitiveDistribution(overridesJSON) {
|
|
|
89171
89172
|
return dist;
|
|
89172
89173
|
}
|
|
89173
89174
|
exports.competitiveDistribution = competitiveDistribution;
|
|
89174
|
-
//
|
|
89175
|
-
//
|
|
89176
|
-
//
|
|
89175
|
+
// The more complex internal range for normalizing Cdf.
|
|
89176
|
+
// * 06/23/2020 - As part of relaxing the competitive range, I changed this max
|
|
89177
|
+
// from 0.67 to 0.75.
|
|
89177
89178
|
function overallCompetitivenessRange(overridesJSON) {
|
|
89178
89179
|
const range = config_json_1.default.partisan.competitiveness.range;
|
|
89179
89180
|
return range;
|
|
@@ -89181,17 +89182,17 @@ function overallCompetitivenessRange(overridesJSON) {
|
|
|
89181
89182
|
exports.overallCompetitivenessRange = overallCompetitivenessRange;
|
|
89182
89183
|
// export function marginalCompetitivenessRange(overridesJSON?: any): number[]
|
|
89183
89184
|
// {
|
|
89184
|
-
// const range = config.partisan.
|
|
89185
|
+
// const range = config.partisan.responsiveness.marginal.range;
|
|
89185
89186
|
// return range;
|
|
89186
89187
|
// }
|
|
89187
89188
|
// export function marginalCompetitivenessWeight(overridesJSON?: any): number
|
|
89188
89189
|
// {
|
|
89189
|
-
// const mcW = config.partisan.
|
|
89190
|
+
// const mcW = config.partisan.responsiveness.marginal.weight;
|
|
89190
89191
|
// return mcW;
|
|
89191
89192
|
// }
|
|
89192
89193
|
// export function overallCompetitivenessWeight(overridesJSON?: any): number
|
|
89193
89194
|
// {
|
|
89194
|
-
// const ocW = config.partisan.
|
|
89195
|
+
// const ocW = config.partisan.responsiveness.overall.weight;
|
|
89195
89196
|
// return ocW;
|
|
89196
89197
|
// }
|
|
89197
89198
|
// MINORITY
|
|
@@ -89215,74 +89216,55 @@ function coalitionDistrictWeight(overridesJSON) {
|
|
|
89215
89216
|
return weight;
|
|
89216
89217
|
}
|
|
89217
89218
|
exports.coalitionDistrictWeight = coalitionDistrictWeight;
|
|
89218
|
-
|
|
89219
|
-
const bonus = config_json_1.default.minority.bonus;
|
|
89220
|
-
return bonus;
|
|
89221
|
-
}
|
|
89222
|
-
exports.minorityBonus = minorityBonus;
|
|
89223
|
-
// TRADITIONAL DISTRICTING PRINCIPLES
|
|
89224
|
-
function compactnessWeight(context, overridesJSON) {
|
|
89225
|
-
const cW = config_json_1.default.traditionalPrinciples.compactness.weight[context];
|
|
89226
|
-
return cW;
|
|
89227
|
-
}
|
|
89228
|
-
exports.compactnessWeight = compactnessWeight;
|
|
89219
|
+
// COMPACTNESS
|
|
89229
89220
|
function reockWeight(overridesJSON) {
|
|
89230
|
-
const rW = config_json_1.default.
|
|
89221
|
+
const rW = config_json_1.default.compactness.reock.weight;
|
|
89231
89222
|
return rW;
|
|
89232
89223
|
}
|
|
89233
89224
|
exports.reockWeight = reockWeight;
|
|
89234
89225
|
function reockRange(overridesJSON) {
|
|
89235
|
-
const range = config_json_1.default.
|
|
89226
|
+
const range = config_json_1.default.compactness.reock.range;
|
|
89236
89227
|
return range;
|
|
89237
89228
|
}
|
|
89238
89229
|
exports.reockRange = reockRange;
|
|
89239
89230
|
function polsbyWeight(overridesJSON) {
|
|
89240
|
-
const ppW = config_json_1.default.
|
|
89231
|
+
const ppW = config_json_1.default.compactness.polsby.weight;
|
|
89241
89232
|
return ppW;
|
|
89242
89233
|
}
|
|
89243
89234
|
exports.polsbyWeight = polsbyWeight;
|
|
89244
89235
|
function polsbyRange(overridesJSON) {
|
|
89245
|
-
const range = config_json_1.default.
|
|
89236
|
+
const range = config_json_1.default.compactness.polsby.range;
|
|
89246
89237
|
return range;
|
|
89247
89238
|
}
|
|
89248
89239
|
exports.polsbyRange = polsbyRange;
|
|
89249
|
-
|
|
89250
|
-
const sW = config_json_1.default.traditionalPrinciples.splitting.weight[context];
|
|
89251
|
-
return sW;
|
|
89252
|
-
}
|
|
89253
|
-
exports.splittingWeight = splittingWeight;
|
|
89240
|
+
// SPLITTING
|
|
89254
89241
|
function countySplittingWeight(overridesJSON) {
|
|
89255
|
-
const csW = config_json_1.default.
|
|
89242
|
+
const csW = config_json_1.default.splitting.county.weight;
|
|
89256
89243
|
return csW;
|
|
89257
89244
|
}
|
|
89258
89245
|
exports.countySplittingWeight = countySplittingWeight;
|
|
89259
89246
|
function countySplittingRange(d, overridesJSON) {
|
|
89260
|
-
const range = config_json_1.default.
|
|
89247
|
+
const range = config_json_1.default.splitting.county.range[d];
|
|
89261
89248
|
return range;
|
|
89262
89249
|
}
|
|
89263
89250
|
exports.countySplittingRange = countySplittingRange;
|
|
89264
89251
|
function districtSplittingWeight(overridesJSON) {
|
|
89265
|
-
const dsW = config_json_1.default.
|
|
89252
|
+
const dsW = config_json_1.default.splitting.district.weight;
|
|
89266
89253
|
return dsW;
|
|
89267
89254
|
}
|
|
89268
89255
|
exports.districtSplittingWeight = districtSplittingWeight;
|
|
89269
89256
|
function districtSplittingRange(d, overridesJSON) {
|
|
89270
|
-
const range = config_json_1.default.
|
|
89257
|
+
const range = config_json_1.default.splitting.district.range[d];
|
|
89271
89258
|
return range;
|
|
89272
89259
|
}
|
|
89273
89260
|
exports.districtSplittingRange = districtSplittingRange;
|
|
89274
|
-
function popdevWeight(context, overridesJSON) {
|
|
89275
|
-
const pdW = config_json_1.default.traditionalPrinciples.popdev.weight[context];
|
|
89276
|
-
return pdW;
|
|
89277
|
-
}
|
|
89278
|
-
exports.popdevWeight = popdevWeight;
|
|
89279
89261
|
// NOTE - Raw ranges, not inverted (i.e., smaller is better)
|
|
89280
89262
|
// NOTE - This could be optimized to not calc LD values for CD's (or do it once)
|
|
89281
89263
|
function popdevRange(bLegislative, overridesJSON) {
|
|
89282
|
-
const cdRange = config_json_1.default.
|
|
89264
|
+
const cdRange = config_json_1.default.popdev.range[0 /* Congressional */];
|
|
89283
89265
|
const worstCD = cdRange[exports.BEG];
|
|
89284
89266
|
const bestCD = cdRange[exports.END];
|
|
89285
|
-
const ldRange = config_json_1.default.
|
|
89267
|
+
const ldRange = config_json_1.default.popdev.range[1 /* StateLegislative */];
|
|
89286
89268
|
const iRange = bLegislative ? ldRange : cdRange;
|
|
89287
89269
|
const worst = iRange[exports.BEG];
|
|
89288
89270
|
const best = bLegislative ? (bestCD / worstCD) * iRange[exports.BEG] : iRange[exports.END];
|
|
@@ -89296,17 +89278,6 @@ function popdevThreshold(bLegislative, overridesJSON) {
|
|
|
89296
89278
|
return threshold;
|
|
89297
89279
|
}
|
|
89298
89280
|
exports.popdevThreshold = popdevThreshold;
|
|
89299
|
-
// OVERALL SCORE
|
|
89300
|
-
function partisanWeight(context, overridesJSON) {
|
|
89301
|
-
const pW = config_json_1.default.partisan.weight[context];
|
|
89302
|
-
return pW;
|
|
89303
|
-
}
|
|
89304
|
-
exports.partisanWeight = partisanWeight;
|
|
89305
|
-
function traditionalPrinciplesWeight(context, overridesJSON) {
|
|
89306
|
-
const tpW = config_json_1.default.traditionalPrinciples.weight[context];
|
|
89307
|
-
return tpW;
|
|
89308
|
-
}
|
|
89309
|
-
exports.traditionalPrinciplesWeight = traditionalPrinciplesWeight;
|
|
89310
89281
|
|
|
89311
89282
|
|
|
89312
89283
|
/***/ }),
|
|
@@ -89431,19 +89402,12 @@ const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
|
89431
89402
|
const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
|
|
89432
89403
|
const partisan_1 = __webpack_require__(/*! ./partisan */ "./src/partisan.ts");
|
|
89433
89404
|
function evalMinorityOpportunity(p, bLog = false) {
|
|
89434
|
-
// Calculate average Democratic win share
|
|
89435
|
-
const VfArray = p.partisanProfile.vfArray;
|
|
89436
|
-
const DWins = VfArray.filter(x => x > 0.5);
|
|
89437
|
-
const RWins = VfArray.filter(x => x <= 0.5); // Ties credited to R's
|
|
89438
|
-
const averageDVf = (DWins.length > 0) ? U.avgArray(DWins) : undefined;
|
|
89439
|
-
const averageRVf = (RWins.length > 0) ? U.avgArray(RWins) : undefined;
|
|
89440
89405
|
// Initialize arrays for results
|
|
89441
89406
|
const offset = 1; // Don't process 'White'
|
|
89442
89407
|
const nDemos = 6 /* Total */; // Ditto
|
|
89443
89408
|
// const nDemos = T.DemographicField.Native + 1 - offset; // Ditto
|
|
89444
89409
|
const demosByDistrict = p.demographicProfile.mfArrayByDistrict;
|
|
89445
89410
|
// Initialize the demographic buckets
|
|
89446
|
-
// COMPETITIVENESS - 06/23/2020 - And populate the statewide values.
|
|
89447
89411
|
// Get the statewide minority VAP/CVAP % (ignore 'White')
|
|
89448
89412
|
const vapPctArray = p.demographicProfile.stateMfArray.slice(1);
|
|
89449
89413
|
// Determine proportional minority districts by demographic (ignoring'White')
|
|
@@ -89486,25 +89450,14 @@ function evalMinorityOpportunity(p, bLog = false) {
|
|
|
89486
89450
|
// The # of opportunity districts for each separate demographic and all minorities
|
|
89487
89451
|
const oD = U.sumArray(opptyByDemo.slice(1)); // Sum individual demos, skipping all minorities
|
|
89488
89452
|
const cD = opptyByDemo[0 /* Minority */]; // All minorities
|
|
89489
|
-
// const cD: number = opptyByDemo[T.DemographicField.Minority - offset]; // All minorities
|
|
89490
89453
|
// The # of proportional districts for each separate demographic and all minorities
|
|
89491
89454
|
const pOd = bucketsByDemo[6 /* Total */][4 /* PropSeats */];
|
|
89492
89455
|
const pCd = bucketsByDemo[0 /* Minority */][4 /* PropSeats */];
|
|
89493
|
-
// const pOd: number = bucketsByDemo[T.DemographicField.Total - 1][T.PivotField.PropSeats];
|
|
89494
|
-
// const pCd: number = bucketsByDemo[T.DemographicField.Minority - 1][T.PivotField.PropSeats];
|
|
89495
|
-
// const pOd: number = U.sumArray(districtsByDemo.slice(1)); // Sum individual demos, skipping all minorities
|
|
89496
|
-
// const pCd: number = districtsByDemo[0]; // All minorities
|
|
89497
89456
|
// Score opportunity
|
|
89498
89457
|
const score = scoreMinority(oD, pOd, cD, pCd);
|
|
89499
89458
|
let mS = {
|
|
89500
|
-
|
|
89501
|
-
averageDVf: averageDVf,
|
|
89502
|
-
averageRVf: averageRVf,
|
|
89503
|
-
bucketsByDemographic: bucketsByDemo
|
|
89504
|
-
},
|
|
89505
|
-
// proportionalOpportunities: pOd,
|
|
89459
|
+
pivotByDemographic: bucketsByDemo,
|
|
89506
89460
|
opportunityDistricts: oD,
|
|
89507
|
-
// proportionalCoalitions: pCd,
|
|
89508
89461
|
coalitionDistricts: cD,
|
|
89509
89462
|
score: score,
|
|
89510
89463
|
details: {}
|
|
@@ -89517,14 +89470,13 @@ exports.evalMinorityOpportunity = evalMinorityOpportunity;
|
|
|
89517
89470
|
// how minority opportunities are estimated (so that 37% minority shares score
|
|
89518
89471
|
// like 52% share).
|
|
89519
89472
|
function scoreMinority(oD, pOd, cD, pCd) {
|
|
89520
|
-
// Score minority opportunity [0–100]
|
|
89521
|
-
const bonus = 100; // C.minorityBonus();
|
|
89473
|
+
// Score minority opportunity [0–100]
|
|
89522
89474
|
const cDWeight = C.coalitionDistrictWeight();
|
|
89523
89475
|
// Cap opportunity & coalition districts
|
|
89524
89476
|
const oDCapped = Math.min(oD, pOd);
|
|
89525
89477
|
const cdCapped = Math.min(cD, pCd);
|
|
89526
|
-
const opportunityScore = (pOd > 0) ? Math.round((oDCapped / pOd) *
|
|
89527
|
-
const coalitionScore = (pCd > 0) ? Math.round((cdCapped / pCd) *
|
|
89478
|
+
const opportunityScore = (pOd > 0) ? Math.round((oDCapped / pOd) * 100) : 0;
|
|
89479
|
+
const coalitionScore = (pCd > 0) ? Math.round((cdCapped / pCd) * 100) : 0;
|
|
89528
89480
|
const score = Math.round(Math.min(opportunityScore + cDWeight * Math.max(coalitionScore - opportunityScore, 0), 100));
|
|
89529
89481
|
return score;
|
|
89530
89482
|
}
|
|
@@ -89565,7 +89517,6 @@ function exceedsMaximumThreshold(Mf) {
|
|
|
89565
89517
|
return Mf > threshold;
|
|
89566
89518
|
}
|
|
89567
89519
|
function calcProportionalDistricts(proportion, nDistricts) {
|
|
89568
|
-
// TODO - Maybe bump up to get a statewide proportion on the bubble to rate one district?
|
|
89569
89520
|
const roundUp = 0.0;
|
|
89570
89521
|
const fractional = proportion * nDistricts;
|
|
89571
89522
|
const integral = Math.round(fractional + roundUp);
|
|
@@ -89617,7 +89568,6 @@ class Normalizer {
|
|
|
89617
89568
|
}
|
|
89618
89569
|
// Invert a value in the unit range [0.0–1.0] (so that bigger is better).
|
|
89619
89570
|
invert() {
|
|
89620
|
-
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
89621
89571
|
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Inverting: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
89622
89572
|
this.wipNum = 1.0 - this.wipNum;
|
|
89623
89573
|
return this.wipNum;
|
|
@@ -89641,7 +89591,6 @@ class Normalizer {
|
|
|
89641
89591
|
unitize(begin_range, end_range) {
|
|
89642
89592
|
const min_range = Math.min(begin_range, end_range);
|
|
89643
89593
|
const max_range = Math.max(begin_range, end_range);
|
|
89644
|
-
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
89645
89594
|
console.assert(((this.wipNum >= min_range) && (this.wipNum <= max_range)), "Unitizing: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
89646
89595
|
const ranged = this.wipNum - min_range;
|
|
89647
89596
|
this.wipNum = Math.abs(ranged / (end_range - begin_range));
|
|
@@ -89651,7 +89600,6 @@ class Normalizer {
|
|
|
89651
89600
|
// NOTE - If the range is already such that "bigger is better," then the closer
|
|
89652
89601
|
// the value is to 1.0 (the best) the *less* it will decay.
|
|
89653
89602
|
decay(fn = 0 /* Gravity */) {
|
|
89654
|
-
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
89655
89603
|
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Decaying: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
89656
89604
|
switch (fn) {
|
|
89657
89605
|
case 0 /* Gravity */: {
|
|
@@ -89665,7 +89613,6 @@ class Normalizer {
|
|
|
89665
89613
|
}
|
|
89666
89614
|
// Translate a value in the unit range to the user-friendly range [0 – 100].
|
|
89667
89615
|
rescale() {
|
|
89668
|
-
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
89669
89616
|
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Rescaling: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
89670
89617
|
this.normalizedNum = Math.round(this.wipNum * S.NORMALIZED_RANGE);
|
|
89671
89618
|
return this.normalizedNum;
|
|
@@ -89702,12 +89649,12 @@ const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
|
89702
89649
|
const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
|
|
89703
89650
|
// NOTE - I'm passing T.VfArray's into everything. District indices = array indices.
|
|
89704
89651
|
// NOTE - I do not (cannot) assume that the values are sorted.
|
|
89652
|
+
// TODO - Revise this.
|
|
89705
89653
|
// SCORE BIAS & COMPETITIVENESS
|
|
89706
89654
|
/* SCORECARD FIELDS:
|
|
89707
89655
|
|
|
89708
89656
|
* ??? [statewideV] (V) = the average statewide two-party vote for Democrats
|
|
89709
89657
|
|
|
89710
|
-
|
|
89711
89658
|
* ^S# [bestS] = the Democratic seats closest to proportional
|
|
89712
89659
|
* ^S% [bestSf] = the corresponding Democratic seat share
|
|
89713
89660
|
* S! [fptpS] = the estimated number of Democratic seats using first past the post
|
|
@@ -89718,7 +89665,7 @@ const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts")
|
|
|
89718
89665
|
* BV_50 [bV50] = Votes bias as a fraction
|
|
89719
89666
|
* decl [decl] = Declination
|
|
89720
89667
|
* GS [gSym] = Global symmetry
|
|
89721
|
-
* ?? [gamma] = TODO
|
|
89668
|
+
* ?? [gamma] = TODO: describe
|
|
89722
89669
|
|
|
89723
89670
|
* EG [EG] = Efficiency gap as a fraction
|
|
89724
89671
|
* BS_V [bSV] = Seats bias @ <V> (geometric)
|
|
@@ -89731,9 +89678,6 @@ const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts")
|
|
|
89731
89678
|
* B% [bias] = the bias calculated as S% – ^S%
|
|
89732
89679
|
* B$ [bias.score] = the bias score normalized [0–100]
|
|
89733
89680
|
|
|
89734
|
-
* UE# [unearnedS] = the number of unearned seats (R = positive; D = negative)
|
|
89735
|
-
* I$ [impact.score] -- the unearned seats normalized [0–100] as impact
|
|
89736
|
-
|
|
89737
89681
|
* R [bigR] = Overall responsiveness or winner’s bonus
|
|
89738
89682
|
* r [littleR] = The point responsiveness at V% (the slope of the S(V) curve at <V>)
|
|
89739
89683
|
* MIR [MIR] = Minimal inverse responsiveness
|
|
@@ -89748,70 +89692,60 @@ const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts")
|
|
|
89748
89692
|
* Md% [mDf] = the probability that the marginal seats will flip, so S% => ^S%
|
|
89749
89693
|
* C$ [competitiveness.score] = the competitiveness score normalized [0–100]
|
|
89750
89694
|
|
|
89751
|
-
* <P$ [score] = the combined partisan score used to compare plans *across* states
|
|
89752
|
-
* >P$ [score2] = the combined partisan score used to compare plans *within* a state, along with traditional districting principles
|
|
89753
|
-
|
|
89754
89695
|
*/
|
|
89755
89696
|
function scorePartisan(Vf, VfArray, options) {
|
|
89756
|
-
const
|
|
89697
|
+
const bAdvanced = options.advanced;
|
|
89757
89698
|
const bConstrained = options.constrained;
|
|
89758
89699
|
const shift = options.shift;
|
|
89759
89700
|
const N = VfArray.length;
|
|
89760
89701
|
const bestS = bestSeats(N, Vf);
|
|
89761
89702
|
const bestSf = bestSeatShare(bestS, N);
|
|
89762
|
-
const fptpS =
|
|
89763
|
-
//
|
|
89703
|
+
const fptpS = bAdvanced ? estFPTPSeats(VfArray) : undefined;
|
|
89704
|
+
// When not constrained, use the full probability distribution to calc metrics.
|
|
89764
89705
|
const range = bConstrained ? C.competitiveDistribution() : undefined;
|
|
89765
89706
|
const estS = estSeats(VfArray, range);
|
|
89766
89707
|
const estSf = estSeatShare(estS, N);
|
|
89767
|
-
const
|
|
89768
|
-
const
|
|
89708
|
+
const deviation = estDeviation(estSf, bestSf);
|
|
89709
|
+
const proportionalityScore = scoreDeviation(deviation, Vf, estSf);
|
|
89769
89710
|
const unearnedS = estUnearnedSeats(bestS, estS);
|
|
89770
89711
|
const impactScore = scoreImpact(unearnedS, Vf, estSf, N);
|
|
89771
89712
|
// Calculate additional alternate metrics for reference
|
|
89772
|
-
// NOTE - Use the uncompressed seat probability function
|
|
89773
89713
|
const dSVpoints = inferSVpoints(Vf, VfArray, shift);
|
|
89774
89714
|
const rSVpoints = invertSVPoints(dSVpoints);
|
|
89775
|
-
// const dSVpoints =
|
|
89776
|
-
const TOf =
|
|
89777
|
-
const Bs50 =
|
|
89715
|
+
// const dSVpoints = bAdvanced ? inferSVpoints(Vf, VfArray, shift) : undefined;
|
|
89716
|
+
const TOf = bAdvanced ? calcTurnoutBias(Vf, VfArray) : undefined;
|
|
89717
|
+
const Bs50 = bAdvanced ? estPartisanBias(dSVpoints, N) : undefined;
|
|
89778
89718
|
const Bs50f = (!(Bs50 === undefined)) ? U.trim(Bs50 / N) : undefined;
|
|
89779
|
-
const Bv50f =
|
|
89780
|
-
const rvPoints =
|
|
89781
|
-
const decl =
|
|
89782
|
-
const gSym =
|
|
89783
|
-
const EG =
|
|
89784
|
-
const BsGf =
|
|
89785
|
-
const prop =
|
|
89786
|
-
const mMs =
|
|
89787
|
-
const mMd =
|
|
89788
|
-
const LO =
|
|
89719
|
+
const Bv50f = bAdvanced ? estVotesBias(dSVpoints, N) : undefined;
|
|
89720
|
+
const rvPoints = bAdvanced ? keyRVpoints(VfArray) : undefined;
|
|
89721
|
+
const decl = bAdvanced ? calcDeclination(VfArray) : undefined;
|
|
89722
|
+
const gSym = bAdvanced ? calcGlobalSymmetry(dSVpoints, rSVpoints, Bs50f) : undefined;
|
|
89723
|
+
const EG = bAdvanced ? calcEfficiencyGap(Vf, estSf) : undefined;
|
|
89724
|
+
const BsGf = bAdvanced ? estGeometricSeatsBias(Vf, dSVpoints, rSVpoints) : undefined;
|
|
89725
|
+
const prop = bAdvanced ? calcDisproportionality(Vf, estSf) : undefined;
|
|
89726
|
+
const mMs = bAdvanced ? estMeanMedianDifference(VfArray, Vf) : undefined;
|
|
89727
|
+
const mMd = bAdvanced ? estMeanMedianDifference(VfArray) : undefined;
|
|
89728
|
+
const LO = bAdvanced ? calcLopsidedOutcomes(VfArray) : undefined;
|
|
89789
89729
|
// Calculate alternate responsiveness metrics for reference
|
|
89790
|
-
const bigR =
|
|
89791
|
-
const littleR =
|
|
89792
|
-
const MIR = (
|
|
89793
|
-
const rD = (!bConstrained ||
|
|
89794
|
-
const rDf =
|
|
89795
|
-
const gamma = (
|
|
89730
|
+
const bigR = bAdvanced ? calcBigR(Vf, estSf) : undefined;
|
|
89731
|
+
const littleR = bAdvanced ? estResponsiveness(Vf, dSVpoints) : undefined;
|
|
89732
|
+
const MIR = (bAdvanced && littleR) ? calcMinimalInverseResponsiveness(Vf, littleR) : undefined;
|
|
89733
|
+
const rD = (!bConstrained || bAdvanced) ? estResponsiveDistricts(VfArray) : undefined;
|
|
89734
|
+
const rDf = bAdvanced ? estResponsiveDistrictsShare(rD, N) : undefined;
|
|
89735
|
+
const gamma = (bAdvanced && littleR) ? calcGamma(Vf, estSf, littleR) : undefined;
|
|
89796
89736
|
const Cn = countCompetitiveDistricts(VfArray);
|
|
89797
89737
|
// NOTE - Cd by definition uses a *possibly* different (more narrow) probability
|
|
89798
89738
|
// distribution than Rd.
|
|
89799
89739
|
const cD = estCompetitiveDistricts(VfArray);
|
|
89800
|
-
// const cD = bConstrained ? estCompetitiveDistricts(VfArray) : rD as number;
|
|
89801
89740
|
const cDf = estCompetitiveDistrictsShare(cD, N);
|
|
89802
89741
|
const competitivenessScore = scoreCompetitiveness(cDf);
|
|
89803
|
-
// NOTE: Original version:
|
|
89804
|
-
// const Mrange = findMarginalDistricts(Vf, VfArray, N);
|
|
89805
|
-
// const Md = estMarginalCompetitiveDistricts(Mrange, VfArray);
|
|
89806
|
-
// const Mdf = estMarginalCompetitiveShare(Md, Mrange);
|
|
89807
|
-
// const competitivenessScore = scoreCompetitiveness(Mdf, cDf);
|
|
89808
89742
|
let biasScoring = {
|
|
89809
89743
|
bestS: bestS,
|
|
89810
89744
|
bestSf: bestSf,
|
|
89811
89745
|
estS: estS,
|
|
89812
89746
|
estSf: estSf,
|
|
89813
|
-
|
|
89814
|
-
score:
|
|
89747
|
+
deviation: deviation,
|
|
89748
|
+
score: proportionalityScore
|
|
89815
89749
|
};
|
|
89816
89750
|
const impactScoring = {
|
|
89817
89751
|
unearnedS: unearnedS,
|
|
@@ -89821,12 +89755,9 @@ function scorePartisan(Vf, VfArray, options) {
|
|
|
89821
89755
|
cSimple: Cn,
|
|
89822
89756
|
cD: cD,
|
|
89823
89757
|
cDf: cDf,
|
|
89824
|
-
// mRange: Mrange,
|
|
89825
|
-
// mD: Md,
|
|
89826
|
-
// mDf: Mdf,
|
|
89827
89758
|
score: competitivenessScore
|
|
89828
89759
|
};
|
|
89829
|
-
if (
|
|
89760
|
+
if (bAdvanced) {
|
|
89830
89761
|
biasScoring.tOf = TOf;
|
|
89831
89762
|
biasScoring.fptpS = fptpS;
|
|
89832
89763
|
biasScoring.bS50 = Bs50f;
|
|
@@ -89847,47 +89778,37 @@ function scorePartisan(Vf, VfArray, options) {
|
|
|
89847
89778
|
competitiveScoring.rD = rD;
|
|
89848
89779
|
competitiveScoring.rDf = rDf;
|
|
89849
89780
|
}
|
|
89850
|
-
|
|
89851
|
-
|
|
89852
|
-
const
|
|
89853
|
-
const
|
|
89854
|
-
const cS = competitiveScoring.score;
|
|
89855
|
-
const acrossStatesPartisanScore = weightPartisan(bS, iS, cS, 0 /* AcrossStates */);
|
|
89856
|
-
const withinStatesPartisanScore = weightPartisan(bS, iS, cS, 1 /* WithinAState */);
|
|
89781
|
+
const DWins = VfArray.filter(x => x > 0.5);
|
|
89782
|
+
const RWins = VfArray.filter(x => x <= 0.5); // Ties credited to R's
|
|
89783
|
+
const averageDVf = (DWins.length > 0) ? U.avgArray(DWins) : undefined;
|
|
89784
|
+
const averageRVf = (RWins.length > 0) ? U.avgArray(RWins) : undefined;
|
|
89857
89785
|
const s = {
|
|
89858
89786
|
bias: biasScoring,
|
|
89859
89787
|
impact: impactScoring,
|
|
89860
|
-
|
|
89788
|
+
responsiveness: competitiveScoring,
|
|
89861
89789
|
dSVpoints: dSVpoints,
|
|
89862
89790
|
rSVpoints: rSVpoints,
|
|
89863
|
-
|
|
89864
|
-
|
|
89791
|
+
averageDVf: averageDVf,
|
|
89792
|
+
averageRVf: averageRVf,
|
|
89865
89793
|
details: {}
|
|
89866
89794
|
};
|
|
89867
89795
|
return s;
|
|
89868
89796
|
}
|
|
89869
89797
|
exports.scorePartisan = scorePartisan;
|
|
89870
|
-
function weightPartisan(bS, iS, cS, context) {
|
|
89871
|
-
const bW = C.biasWeight(context);
|
|
89872
|
-
const iW = C.impactWeight(context);
|
|
89873
|
-
const cW = C.competitivenessWeight(context);
|
|
89874
|
-
const score = Math.round(((bS * bW) + (iS * iW) + (cS * cW)) / (bW + iW + cW));
|
|
89875
|
-
return score;
|
|
89876
|
-
}
|
|
89877
|
-
exports.weightPartisan = weightPartisan;
|
|
89878
89798
|
function extraBonus(Vf) {
|
|
89879
|
-
const
|
|
89799
|
+
const over50Pct = (Vf > 0.5) ? (Vf - 0.5) : (0.5 - Vf);
|
|
89800
|
+
const okExtra = over50Pct * (C.winnerBonus() - 1.0);
|
|
89880
89801
|
return U.trim(okExtra);
|
|
89881
89802
|
}
|
|
89882
89803
|
exports.extraBonus = extraBonus;
|
|
89883
|
-
function
|
|
89804
|
+
function scoreDeviation(deviation, Vf, Sf) {
|
|
89884
89805
|
if (isAntimajoritarian(Vf, Sf)) {
|
|
89885
89806
|
return 0;
|
|
89886
89807
|
}
|
|
89887
89808
|
else {
|
|
89888
89809
|
// Adjust bias to incorporate an acceptable winner's bonus based on Vf
|
|
89889
89810
|
const extra = extraBonus(Vf);
|
|
89890
|
-
const adjusted =
|
|
89811
|
+
const adjusted = adjustDeviation(Vf, deviation, extra);
|
|
89891
89812
|
// Then normalize
|
|
89892
89813
|
const _normalizer = new normalize_1.Normalizer(adjusted);
|
|
89893
89814
|
const worst = C.biasRange()[C.BEG];
|
|
@@ -89901,15 +89822,24 @@ function scorebias(rawBias, Vf, Sf) {
|
|
|
89901
89822
|
return score;
|
|
89902
89823
|
}
|
|
89903
89824
|
}
|
|
89904
|
-
exports.
|
|
89905
|
-
// Adjust
|
|
89906
|
-
|
|
89907
|
-
|
|
89908
|
-
|
|
89909
|
-
|
|
89910
|
-
|
|
89825
|
+
exports.scoreDeviation = scoreDeviation;
|
|
89826
|
+
// Adjust deviation from proportionality to account for a winner's bonus
|
|
89827
|
+
// * If the bias is in the *same* direction as the statewide vote %, then
|
|
89828
|
+
// discount the bias by the winner's bonus (extra).
|
|
89829
|
+
// * But if the bias and statewide vote % go in opposite directions, leave the
|
|
89830
|
+
// bias unadjusted.
|
|
89831
|
+
function adjustDeviation(Vf, deviation, extra) {
|
|
89832
|
+
let adjusted = deviation;
|
|
89833
|
+
if ((Vf > 0.5) && (deviation < 0)) {
|
|
89834
|
+
adjusted = Math.min(deviation + extra, 0);
|
|
89835
|
+
}
|
|
89836
|
+
else if ((Vf < 0.5) && (deviation > 0)) {
|
|
89837
|
+
adjusted = Math.max(deviation - extra, 0);
|
|
89838
|
+
}
|
|
89839
|
+
return adjusted;
|
|
89911
89840
|
}
|
|
89912
|
-
exports.
|
|
89841
|
+
exports.adjustDeviation = adjustDeviation;
|
|
89842
|
+
// NOTE - Not used.
|
|
89913
89843
|
// Normalize unearned seats
|
|
89914
89844
|
function scoreImpact(rawUE, Vf, Sf, N) {
|
|
89915
89845
|
if (isAntimajoritarian(Vf, Sf)) {
|
|
@@ -89918,7 +89848,7 @@ function scoreImpact(rawUE, Vf, Sf, N) {
|
|
|
89918
89848
|
else {
|
|
89919
89849
|
// Adjust impact to incorporate an acceptable winner's bonus based on Vf
|
|
89920
89850
|
const extra = extraBonus(Vf);
|
|
89921
|
-
const adjustedBias =
|
|
89851
|
+
const adjustedBias = adjustDeviation(Vf, rawUE / N, extra);
|
|
89922
89852
|
const adjustedImpact = adjustedBias * N;
|
|
89923
89853
|
// Then normalize
|
|
89924
89854
|
const _normalizer = new normalize_1.Normalizer(adjustedImpact);
|
|
@@ -89941,9 +89871,8 @@ function isAntimajoritarian(Vf, Sf) {
|
|
|
89941
89871
|
return bDem || bRep;
|
|
89942
89872
|
}
|
|
89943
89873
|
exports.isAntimajoritarian = isAntimajoritarian;
|
|
89944
|
-
// COMPETITIVENESS - Revised 06/23/2020
|
|
89945
89874
|
// Normalize overall competitiveness - Raw values are in the range [0.0–1.0].
|
|
89946
|
-
// But the practical max is more like
|
|
89875
|
+
// But the practical max is more like 3/4's, so unitize that range to [0.0–1.0].
|
|
89947
89876
|
// Then scale the values to [0–100].
|
|
89948
89877
|
function scoreCompetitiveness(Cdf) {
|
|
89949
89878
|
const _normalizer = new normalize_1.Normalizer(Cdf);
|
|
@@ -90036,8 +89965,6 @@ function inferSVpoints(Vf, VfArray, shift, range) {
|
|
|
90036
89965
|
const shiftedVPI = shiftDistricts(Vf, VfArray, shiftedVf, shift);
|
|
90037
89966
|
const shiftedSf = estSeats(shiftedVPI, range) / nDistricts;
|
|
90038
89967
|
SVpoints.push({ v: shiftedVf, s: shiftedSf });
|
|
90039
|
-
// TODO - Why can't I trim these? Why does that only break the Hypotheticals?!?
|
|
90040
|
-
// SVpoints.push({v: U.trim(Number(shiftedVf)), s: shiftedSf});
|
|
90041
89968
|
}
|
|
90042
89969
|
return SVpoints;
|
|
90043
89970
|
}
|
|
@@ -90122,11 +90049,12 @@ function estFPTPSeats(VfArray) {
|
|
|
90122
90049
|
}));
|
|
90123
90050
|
}
|
|
90124
90051
|
exports.estFPTPSeats = estFPTPSeats;
|
|
90125
|
-
// B% - The
|
|
90126
|
-
function
|
|
90052
|
+
// B% - The deviation from proportionality calculated as ^S% — S%
|
|
90053
|
+
function estDeviation(estSf, bestSf) {
|
|
90127
90054
|
return U.trim(bestSf - estSf);
|
|
90128
90055
|
}
|
|
90129
|
-
exports.
|
|
90056
|
+
exports.estDeviation = estDeviation;
|
|
90057
|
+
// NOTE - Not used.
|
|
90130
90058
|
// UE# - The estimated # of unearned seats
|
|
90131
90059
|
// UE_# from http://bit.ly/2Fcuf4q
|
|
90132
90060
|
function estUnearnedSeats(proportional, probable) {
|
|
@@ -90231,11 +90159,9 @@ function estCompetitiveDistricts(VfArray, bCompress = false) {
|
|
|
90231
90159
|
return U.trim(U.sumArray(VfArray.map(v => estDistrictCompetitiveness(v, bCompress))));
|
|
90232
90160
|
}
|
|
90233
90161
|
exports.estCompetitiveDistricts = estCompetitiveDistricts;
|
|
90234
|
-
// COMPETITIVENESS - Modified 06/22/2020
|
|
90235
|
-
// Re-scale a Democratic vote share to the competitive range (e.g., 45–55%).
|
|
90236
90162
|
function estDistrictCompetitiveness(Vf, bCompress = false) {
|
|
90237
90163
|
const _normalizer = new normalize_1.Normalizer(Vf);
|
|
90238
|
-
// The end points of
|
|
90164
|
+
// The end points of the probability distribution
|
|
90239
90165
|
// NOTE - These aren't the points where races start or stop being contested,
|
|
90240
90166
|
// just the end points of a distribution that yields the desired behavior
|
|
90241
90167
|
// in the typical competitive range [45-55%].
|
|
@@ -90460,9 +90386,6 @@ function keyRVpoints(VfArray) {
|
|
|
90460
90386
|
const nDistricts = VfArray.length;
|
|
90461
90387
|
const estS = estSeats(VfArray);
|
|
90462
90388
|
const Sb = estSeatShare(estS, nDistricts);
|
|
90463
|
-
// TODO - Understand why the corresponding V to Sb is always @ 0.5.
|
|
90464
|
-
// John Nagle: "This is the dividing vote for party A vs party B wins defined
|
|
90465
|
-
// by Warrington. My modification just puts fractions of districts to the each side."
|
|
90466
90389
|
const Rb = Sb / 2;
|
|
90467
90390
|
const Ra = (1 + Sb) / 2;
|
|
90468
90391
|
const Vb = 1.0 - (U.sumArray(VfArray.map(v => estSeatProbability(v) * v))) / estS;
|
|
@@ -90602,23 +90525,19 @@ function calcGamma(Vf, Sf, r) {
|
|
|
90602
90525
|
exports.calcGamma = calcGamma;
|
|
90603
90526
|
// HELPERS
|
|
90604
90527
|
function printPartisanScorecardHeader() {
|
|
90605
|
-
console.log('XX, Name, N, V%, ^S#, S#, B%, B$,
|
|
90528
|
+
console.log('XX, Name, N, V%, ^S#, S#, B%, B$, C#, Cd, Cdf, C$');
|
|
90606
90529
|
}
|
|
90607
90530
|
exports.printPartisanScorecardHeader = printPartisanScorecardHeader;
|
|
90608
90531
|
function printPartisanScorecardRow(xx, name, N, Vf, s) {
|
|
90609
|
-
console.log('%s, %s, %i, %f, %i, %f, %f, %i, %
|
|
90532
|
+
console.log('%s, %s, %i, %f, %i, %f, %f, %i, %i, %f, %f, %i', xx, // 1
|
|
90610
90533
|
name, // 2
|
|
90611
90534
|
N, // 3
|
|
90612
90535
|
Vf, // 4
|
|
90613
90536
|
s.bias.bestS, // 5
|
|
90614
90537
|
s.bias.estS, // 6
|
|
90615
|
-
s.bias.
|
|
90616
|
-
s.bias.score, s.
|
|
90617
|
-
s.
|
|
90618
|
-
s.competitiveness.cD, s.competitiveness.cDf,
|
|
90619
|
-
// s.competitiveness.mD,
|
|
90620
|
-
s.competitiveness.score, s.score // 15
|
|
90621
|
-
);
|
|
90538
|
+
s.bias.deviation, // 7
|
|
90539
|
+
s.bias.score, s.responsiveness.cSimple, // 9
|
|
90540
|
+
s.responsiveness.cD, s.responsiveness.cDf, s.responsiveness.score);
|
|
90622
90541
|
}
|
|
90623
90542
|
exports.printPartisanScorecardRow = printPartisanScorecardRow;
|
|
90624
90543
|
// Generate partisan details (Table 1)
|
|
@@ -90627,12 +90546,12 @@ function printPartisanDetailsHeader() {
|
|
|
90627
90546
|
}
|
|
90628
90547
|
exports.printPartisanDetailsHeader = printPartisanDetailsHeader;
|
|
90629
90548
|
function printPartisanDetailsRow(xx, name, N, Vf, s) {
|
|
90630
|
-
console.log('%s, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f', xx, Vf, s.bias.estSf, s.bias.
|
|
90549
|
+
console.log('%s, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f', xx, Vf, s.bias.estSf, s.bias.deviation, // Simple deviation from proportionality
|
|
90631
90550
|
s.bias.bS50, s.bias.bV50, s.bias.decl, s.bias.gSym, s.bias.eG, s.bias.bSV, // Beta
|
|
90632
90551
|
s.bias.prop, // PR
|
|
90633
|
-
s.bias.mMs, s.bias.tOf, s.bias.mMd, s.bias.lO, s.
|
|
90634
|
-
s.
|
|
90635
|
-
s.
|
|
90552
|
+
s.bias.mMs, s.bias.tOf, s.bias.mMd, s.bias.lO, s.responsiveness.rD, s.responsiveness.bigR, s.responsiveness.littleR, s.responsiveness.mIR, // Zeta
|
|
90553
|
+
s.responsiveness.cD, // COMPETITIVENESS - Temporary to confirm new calc
|
|
90554
|
+
s.responsiveness.cDf);
|
|
90636
90555
|
}
|
|
90637
90556
|
exports.printPartisanDetailsRow = printPartisanDetailsRow;
|
|
90638
90557
|
|
|
@@ -90659,7 +90578,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
90659
90578
|
return result;
|
|
90660
90579
|
};
|
|
90661
90580
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90662
|
-
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
90663
90581
|
const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
90664
90582
|
const compact_1 = __webpack_require__(/*! ./compact */ "./src/compact.ts");
|
|
90665
90583
|
const cohesive_1 = __webpack_require__(/*! ./cohesive */ "./src/cohesive.ts");
|
|
@@ -90675,7 +90593,8 @@ function scorePlan(p, overridesJSON) {
|
|
|
90675
90593
|
const cS = {
|
|
90676
90594
|
score: compactnessScore,
|
|
90677
90595
|
reock: reockM,
|
|
90678
|
-
polsby: polsbyM
|
|
90596
|
+
polsby: polsbyM,
|
|
90597
|
+
details: {}
|
|
90679
90598
|
};
|
|
90680
90599
|
// Splitting
|
|
90681
90600
|
const CxD = p.splittingProfile.countyPopByDistrict;
|
|
@@ -90687,40 +90606,26 @@ function scorePlan(p, overridesJSON) {
|
|
|
90687
90606
|
const sS = {
|
|
90688
90607
|
score: splittingScore,
|
|
90689
90608
|
county: countyM,
|
|
90690
|
-
district: districtM
|
|
90609
|
+
district: districtM,
|
|
90610
|
+
details: {}
|
|
90691
90611
|
};
|
|
90692
90612
|
// Population deviation
|
|
90693
90613
|
const pdS = equal_1.doPopulationDeviation(p.populationProfile.totalPopByDistrict, p.populationProfile.targetSize, p.legislativeDistricts);
|
|
90694
|
-
//
|
|
90695
|
-
const tpScore = weightTradtionalPrinciples(cS.score, sS.score, pdS.normalized, 1 /* WithinAState */);
|
|
90696
|
-
// Populate the "best" traditional principles scorecard
|
|
90697
|
-
const tpS = {
|
|
90698
|
-
score: tpScore,
|
|
90699
|
-
compactness: cS,
|
|
90700
|
-
splitting: sS,
|
|
90701
|
-
populationDeviation: pdS,
|
|
90702
|
-
details: {}
|
|
90703
|
-
};
|
|
90704
|
-
// PARTISAN ("fair") subcategories - bias, impact, & competitiveness (plus lots of supporting measures)
|
|
90614
|
+
// Partisan - bias & responsiveness
|
|
90705
90615
|
const options = {
|
|
90706
|
-
|
|
90616
|
+
advanced: true,
|
|
90707
90617
|
constrained: false,
|
|
90708
90618
|
shift: 0 /* Proportional */
|
|
90709
90619
|
};
|
|
90710
90620
|
const pS = partisan_1.scorePartisan(p.partisanProfile.statewideVf, p.partisanProfile.vfArray, options);
|
|
90711
|
-
// Combine the partisan/partisan & traditional principles/best scores into an overall
|
|
90712
|
-
// score for comparing plans w/in a state
|
|
90713
|
-
let score = weightOverall(pS.score2, tpS.score, 1 /* WithinAState */);
|
|
90714
90621
|
const mS = minority_1.evalMinorityOpportunity(p);
|
|
90715
|
-
//
|
|
90716
|
-
const bonus = C.minorityBonus() * (mS.score / 100);
|
|
90717
|
-
score = mixinMinorityBonus(score, bonus);
|
|
90718
|
-
// Roll up an overall scorecard
|
|
90622
|
+
// Combine the pieces into a scorecard
|
|
90719
90623
|
const scorecard = {
|
|
90720
90624
|
partisan: pS,
|
|
90721
90625
|
minority: mS,
|
|
90722
|
-
|
|
90723
|
-
|
|
90626
|
+
compactness: cS,
|
|
90627
|
+
splitting: sS,
|
|
90628
|
+
populationDeviation: pdS,
|
|
90724
90629
|
details: {}
|
|
90725
90630
|
};
|
|
90726
90631
|
return scorecard;
|
|
@@ -90741,44 +90646,20 @@ function weightSplitting(csS, dsS) {
|
|
|
90741
90646
|
return score;
|
|
90742
90647
|
}
|
|
90743
90648
|
exports.weightSplitting = weightSplitting;
|
|
90744
|
-
function weightTradtionalPrinciples(cS, sS, pdS, context) {
|
|
90745
|
-
const cW = C.compactnessWeight(context);
|
|
90746
|
-
const sW = C.splittingWeight(context);
|
|
90747
|
-
const pdW = C.popdevWeight(context);
|
|
90748
|
-
const score = Math.round(((cS * cW) + (sS * sW) + (pdS * pdW)) / (cW + sW + pdW));
|
|
90749
|
-
return score;
|
|
90750
|
-
}
|
|
90751
|
-
exports.weightTradtionalPrinciples = weightTradtionalPrinciples;
|
|
90752
|
-
function weightOverall(pS, tpS, context) {
|
|
90753
|
-
const pW = C.partisanWeight(context);
|
|
90754
|
-
const tpW = C.traditionalPrinciplesWeight(context);
|
|
90755
|
-
const score = Math.round(((pS * pW) + (tpS * tpW)) / (pW + tpW));
|
|
90756
|
-
return score;
|
|
90757
|
-
}
|
|
90758
|
-
exports.weightOverall = weightOverall;
|
|
90759
|
-
function mixinMinorityBonus(score, minorityBonus) {
|
|
90760
|
-
const modifiedScore = Math.min(score + minorityBonus, S.NORMALIZED_RANGE);
|
|
90761
|
-
return modifiedScore;
|
|
90762
|
-
}
|
|
90763
|
-
exports.mixinMinorityBonus = mixinMinorityBonus;
|
|
90764
90649
|
function printScorecardHeader() {
|
|
90765
|
-
console.log('XX, Name, N, V%, ^S#, S#, B%, B$,
|
|
90650
|
+
console.log('XX, Name, N, V%, ^S#, S#, B%, B$, C#, Cd, Cdf, C$, Rc, Rc$, Pc, Pc$, G$, Cs, Cs$, Ds, Ds$, S$, Eq, Eq$, Od, Md, M$');
|
|
90766
90651
|
}
|
|
90767
90652
|
exports.printScorecardHeader = printScorecardHeader;
|
|
90768
90653
|
function printScorecardRow(xx, name, N, Vf, s) {
|
|
90769
|
-
console.log('%s, %s, %i, %f, %i, %f, %f, %i, %
|
|
90654
|
+
console.log('%s, %s, %i, %f, %i, %f, %f, %i, %i, %f, %f, %i, %f, %i, %f, %i, %i, %f, %i, %f, %i, %i, %f, %i, %f, %f, %i', xx, // 1
|
|
90770
90655
|
name, // 2
|
|
90771
90656
|
N, // 3
|
|
90772
90657
|
Vf, // 4
|
|
90773
90658
|
s.partisan.bias.bestS, // 5
|
|
90774
90659
|
s.partisan.bias.estS, // 6
|
|
90775
|
-
s.partisan.bias.
|
|
90776
|
-
s.partisan.bias.score, s.partisan.
|
|
90777
|
-
s.partisan.
|
|
90778
|
-
s.partisan.competitiveness.cD, s.partisan.competitiveness.cDf,
|
|
90779
|
-
// s.partisan.competitiveness.mD,
|
|
90780
|
-
s.partisan.competitiveness.score, s.partisan.score2, // 15
|
|
90781
|
-
s.traditionalPrinciples.compactness.reock.raw, s.traditionalPrinciples.compactness.reock.normalized, s.traditionalPrinciples.compactness.polsby.raw, s.traditionalPrinciples.compactness.polsby.normalized, s.traditionalPrinciples.compactness.score, s.traditionalPrinciples.splitting.county.raw, s.traditionalPrinciples.splitting.county.normalized, s.traditionalPrinciples.splitting.district.raw, s.traditionalPrinciples.splitting.district.normalized, s.traditionalPrinciples.splitting.score, s.traditionalPrinciples.populationDeviation.raw, s.traditionalPrinciples.populationDeviation.normalized, s.traditionalPrinciples.score, s.minority.opportunityDistricts, s.minority.coalitionDistricts, s.minority.score, s.score);
|
|
90660
|
+
s.partisan.bias.deviation, // 7
|
|
90661
|
+
s.partisan.bias.score, s.partisan.responsiveness.cSimple, // 9
|
|
90662
|
+
s.partisan.responsiveness.cD, s.partisan.responsiveness.cDf, s.partisan.responsiveness.score, s.compactness.reock.raw, s.compactness.reock.normalized, s.compactness.polsby.raw, s.compactness.polsby.normalized, s.compactness.score, s.splitting.county.raw, s.splitting.county.normalized, s.splitting.district.raw, s.splitting.district.normalized, s.splitting.score, s.populationDeviation.raw, s.populationDeviation.normalized, s.minority.opportunityDistricts, s.minority.coalitionDistricts, s.minority.score);
|
|
90782
90663
|
}
|
|
90783
90664
|
exports.printScorecardRow = printScorecardRow;
|
|
90784
90665
|
|
|
@@ -102927,10 +102808,10 @@ class AnalyticsSession {
|
|
|
102927
102808
|
const scorecard = this._scorecard;
|
|
102928
102809
|
const r = {
|
|
102929
102810
|
proportionality: scorecard.partisan.bias.score,
|
|
102930
|
-
competitiveness: scorecard.partisan.
|
|
102811
|
+
competitiveness: scorecard.partisan.responsiveness.score,
|
|
102931
102812
|
minorityRights: scorecard.minority.score,
|
|
102932
|
-
compactness: scorecard.
|
|
102933
|
-
splitting: scorecard.
|
|
102813
|
+
compactness: scorecard.compactness.score,
|
|
102814
|
+
splitting: scorecard.splitting.score
|
|
102934
102815
|
};
|
|
102935
102816
|
return r;
|
|
102936
102817
|
}
|
|
@@ -104428,7 +104309,7 @@ function doAnalyzePostProcessing(s, bLog = false) {
|
|
|
104428
104309
|
// Just populate the normalized population deviation score in the test
|
|
104429
104310
|
const scorecard = s._scorecard;
|
|
104430
104311
|
let popDev = s.getTest(4 /* PopulationDeviation */);
|
|
104431
|
-
popDev['normalizedScore'] = scorecard.
|
|
104312
|
+
popDev['normalizedScore'] = scorecard.populationDeviation.normalized;
|
|
104432
104313
|
const datasets = {
|
|
104433
104314
|
shapes: S.SHAPES,
|
|
104434
104315
|
census: U.deepCopy(s.config['descriptions']['CENSUS']),
|
|
@@ -104437,12 +104318,12 @@ function doAnalyzePostProcessing(s, bLog = false) {
|
|
|
104437
104318
|
};
|
|
104438
104319
|
scorecard.partisan.details['election'] = datasets.election;
|
|
104439
104320
|
scorecard.minority.details['vap'] = datasets.vap;
|
|
104440
|
-
scorecard.
|
|
104441
|
-
scorecard.
|
|
104321
|
+
scorecard.details['shapes'] = datasets.shapes;
|
|
104322
|
+
scorecard.details['census'] = datasets.census;
|
|
104442
104323
|
const simpleSplits = s.getTest(5 /* UnexpectedCountySplits */);
|
|
104443
|
-
scorecard.
|
|
104444
|
-
scorecard.
|
|
104445
|
-
scorecard.
|
|
104324
|
+
scorecard.compactness.details['unexpectedAffected'] = simpleSplits['score'];
|
|
104325
|
+
scorecard.compactness.details['nSplits'] = simpleSplits['details']['nSplits'];
|
|
104326
|
+
scorecard.compactness.details['countiesSplitUnexpectedly'] = U.deepCopy(simpleSplits['details']['countiesSplitUnexpectedly']);
|
|
104446
104327
|
// NOTE - Add split precincts in dra-client directly
|
|
104447
104328
|
// Derive secondary tests
|
|
104448
104329
|
analyze_1.doDeriveSecondaryTests(s, bLog);
|
|
@@ -104587,10 +104468,10 @@ function scorePlan(s, p, bLog = false, overridesJSON) {
|
|
|
104587
104468
|
// doHasEqualPopulations() to use later.This is preserving the old calling sequence.
|
|
104588
104469
|
let test = s.getTest(4 /* PopulationDeviation */);
|
|
104589
104470
|
// Get the raw population deviation
|
|
104590
|
-
const popDev = scorecard.
|
|
104471
|
+
const popDev = scorecard.populationDeviation.raw;
|
|
104591
104472
|
// Populate the test entry
|
|
104592
104473
|
test['score'] = popDev;
|
|
104593
|
-
test['details'] = { 'maxDeviation': scorecard.
|
|
104474
|
+
test['details'] = { 'maxDeviation': scorecard.populationDeviation.notes['maxDeviation'] };
|
|
104594
104475
|
// Populate the N+1 summary "district" in district.statistics
|
|
104595
104476
|
let totalPop = s.districts.statistics[D.DistrictField.TotalPop];
|
|
104596
104477
|
let popDevPct = s.districts.statistics[D.DistrictField.PopDevPct];
|