@dra2020/district-analytics 6.2.0 → 7.0.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 +131 -253
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +13 -11
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/types.d.ts +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,18 +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
|
-
const nDemos = 6 /*
|
|
89407
|
+
const nDemos = 6 /* Total */; // Ditto
|
|
89408
|
+
// const nDemos = T.DemographicField.Native + 1 - offset; // Ditto
|
|
89443
89409
|
const demosByDistrict = p.demographicProfile.mfArrayByDistrict;
|
|
89444
89410
|
// Initialize the demographic buckets
|
|
89445
|
-
// COMPETITIVENESS - 06/23/2020 - And populate the statewide values.
|
|
89446
89411
|
// Get the statewide minority VAP/CVAP % (ignore 'White')
|
|
89447
89412
|
const vapPctArray = p.demographicProfile.stateMfArray.slice(1);
|
|
89448
89413
|
// Determine proportional minority districts by demographic (ignoring'White')
|
|
@@ -89484,23 +89449,15 @@ function evalMinorityOpportunity(p, bLog = false) {
|
|
|
89484
89449
|
}
|
|
89485
89450
|
// The # of opportunity districts for each separate demographic and all minorities
|
|
89486
89451
|
const oD = U.sumArray(opptyByDemo.slice(1)); // Sum individual demos, skipping all minorities
|
|
89487
|
-
const cD = opptyByDemo[
|
|
89452
|
+
const cD = opptyByDemo[0 /* Minority */]; // All minorities
|
|
89488
89453
|
// The # of proportional districts for each separate demographic and all minorities
|
|
89489
|
-
const pOd = bucketsByDemo[
|
|
89490
|
-
const pCd = bucketsByDemo[
|
|
89491
|
-
// const pOd: number = U.sumArray(districtsByDemo.slice(1)); // Sum individual demos, skipping all minorities
|
|
89492
|
-
// const pCd: number = districtsByDemo[0]; // All minorities
|
|
89454
|
+
const pOd = bucketsByDemo[6 /* Total */][4 /* PropSeats */];
|
|
89455
|
+
const pCd = bucketsByDemo[0 /* Minority */][4 /* PropSeats */];
|
|
89493
89456
|
// Score opportunity
|
|
89494
89457
|
const score = scoreMinority(oD, pOd, cD, pCd);
|
|
89495
89458
|
let mS = {
|
|
89496
|
-
|
|
89497
|
-
averageDVf: averageDVf,
|
|
89498
|
-
averageRVf: averageRVf,
|
|
89499
|
-
bucketsByDemographic: bucketsByDemo
|
|
89500
|
-
},
|
|
89501
|
-
// proportionalOpportunities: pOd,
|
|
89459
|
+
pivotByDemographic: bucketsByDemo,
|
|
89502
89460
|
opportunityDistricts: oD,
|
|
89503
|
-
// proportionalCoalitions: pCd,
|
|
89504
89461
|
coalitionDistricts: cD,
|
|
89505
89462
|
score: score,
|
|
89506
89463
|
details: {}
|
|
@@ -89513,14 +89470,13 @@ exports.evalMinorityOpportunity = evalMinorityOpportunity;
|
|
|
89513
89470
|
// how minority opportunities are estimated (so that 37% minority shares score
|
|
89514
89471
|
// like 52% share).
|
|
89515
89472
|
function scoreMinority(oD, pOd, cD, pCd) {
|
|
89516
|
-
// Score minority opportunity [0–100]
|
|
89517
|
-
const bonus = 100; // C.minorityBonus();
|
|
89473
|
+
// Score minority opportunity [0–100]
|
|
89518
89474
|
const cDWeight = C.coalitionDistrictWeight();
|
|
89519
89475
|
// Cap opportunity & coalition districts
|
|
89520
89476
|
const oDCapped = Math.min(oD, pOd);
|
|
89521
89477
|
const cdCapped = Math.min(cD, pCd);
|
|
89522
|
-
const opportunityScore = (pOd > 0) ? Math.round((oDCapped / pOd) *
|
|
89523
|
-
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;
|
|
89524
89480
|
const score = Math.round(Math.min(opportunityScore + cDWeight * Math.max(coalitionScore - opportunityScore, 0), 100));
|
|
89525
89481
|
return score;
|
|
89526
89482
|
}
|
|
@@ -89561,7 +89517,6 @@ function exceedsMaximumThreshold(Mf) {
|
|
|
89561
89517
|
return Mf > threshold;
|
|
89562
89518
|
}
|
|
89563
89519
|
function calcProportionalDistricts(proportion, nDistricts) {
|
|
89564
|
-
// TODO - Maybe bump up to get a statewide proportion on the bubble to rate one district?
|
|
89565
89520
|
const roundUp = 0.0;
|
|
89566
89521
|
const fractional = proportion * nDistricts;
|
|
89567
89522
|
const integral = Math.round(fractional + roundUp);
|
|
@@ -89613,7 +89568,6 @@ class Normalizer {
|
|
|
89613
89568
|
}
|
|
89614
89569
|
// Invert a value in the unit range [0.0–1.0] (so that bigger is better).
|
|
89615
89570
|
invert() {
|
|
89616
|
-
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
89617
89571
|
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Inverting: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
89618
89572
|
this.wipNum = 1.0 - this.wipNum;
|
|
89619
89573
|
return this.wipNum;
|
|
@@ -89637,7 +89591,6 @@ class Normalizer {
|
|
|
89637
89591
|
unitize(begin_range, end_range) {
|
|
89638
89592
|
const min_range = Math.min(begin_range, end_range);
|
|
89639
89593
|
const max_range = Math.max(begin_range, end_range);
|
|
89640
|
-
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
89641
89594
|
console.assert(((this.wipNum >= min_range) && (this.wipNum <= max_range)), "Unitizing: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
89642
89595
|
const ranged = this.wipNum - min_range;
|
|
89643
89596
|
this.wipNum = Math.abs(ranged / (end_range - begin_range));
|
|
@@ -89647,7 +89600,6 @@ class Normalizer {
|
|
|
89647
89600
|
// NOTE - If the range is already such that "bigger is better," then the closer
|
|
89648
89601
|
// the value is to 1.0 (the best) the *less* it will decay.
|
|
89649
89602
|
decay(fn = 0 /* Gravity */) {
|
|
89650
|
-
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
89651
89603
|
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Decaying: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
89652
89604
|
switch (fn) {
|
|
89653
89605
|
case 0 /* Gravity */: {
|
|
@@ -89661,7 +89613,6 @@ class Normalizer {
|
|
|
89661
89613
|
}
|
|
89662
89614
|
// Translate a value in the unit range to the user-friendly range [0 – 100].
|
|
89663
89615
|
rescale() {
|
|
89664
|
-
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
89665
89616
|
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Rescaling: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
89666
89617
|
this.normalizedNum = Math.round(this.wipNum * S.NORMALIZED_RANGE);
|
|
89667
89618
|
return this.normalizedNum;
|
|
@@ -89698,12 +89649,12 @@ const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
|
89698
89649
|
const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
|
|
89699
89650
|
// NOTE - I'm passing T.VfArray's into everything. District indices = array indices.
|
|
89700
89651
|
// NOTE - I do not (cannot) assume that the values are sorted.
|
|
89652
|
+
// TODO - Revise this.
|
|
89701
89653
|
// SCORE BIAS & COMPETITIVENESS
|
|
89702
89654
|
/* SCORECARD FIELDS:
|
|
89703
89655
|
|
|
89704
89656
|
* ??? [statewideV] (V) = the average statewide two-party vote for Democrats
|
|
89705
89657
|
|
|
89706
|
-
|
|
89707
89658
|
* ^S# [bestS] = the Democratic seats closest to proportional
|
|
89708
89659
|
* ^S% [bestSf] = the corresponding Democratic seat share
|
|
89709
89660
|
* S! [fptpS] = the estimated number of Democratic seats using first past the post
|
|
@@ -89714,7 +89665,7 @@ const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts")
|
|
|
89714
89665
|
* BV_50 [bV50] = Votes bias as a fraction
|
|
89715
89666
|
* decl [decl] = Declination
|
|
89716
89667
|
* GS [gSym] = Global symmetry
|
|
89717
|
-
* ?? [gamma] = TODO
|
|
89668
|
+
* ?? [gamma] = TODO: describe
|
|
89718
89669
|
|
|
89719
89670
|
* EG [EG] = Efficiency gap as a fraction
|
|
89720
89671
|
* BS_V [bSV] = Seats bias @ <V> (geometric)
|
|
@@ -89727,9 +89678,6 @@ const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts")
|
|
|
89727
89678
|
* B% [bias] = the bias calculated as S% – ^S%
|
|
89728
89679
|
* B$ [bias.score] = the bias score normalized [0–100]
|
|
89729
89680
|
|
|
89730
|
-
* UE# [unearnedS] = the number of unearned seats (R = positive; D = negative)
|
|
89731
|
-
* I$ [impact.score] -- the unearned seats normalized [0–100] as impact
|
|
89732
|
-
|
|
89733
89681
|
* R [bigR] = Overall responsiveness or winner’s bonus
|
|
89734
89682
|
* r [littleR] = The point responsiveness at V% (the slope of the S(V) curve at <V>)
|
|
89735
89683
|
* MIR [MIR] = Minimal inverse responsiveness
|
|
@@ -89744,85 +89692,66 @@ const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts")
|
|
|
89744
89692
|
* Md% [mDf] = the probability that the marginal seats will flip, so S% => ^S%
|
|
89745
89693
|
* C$ [competitiveness.score] = the competitiveness score normalized [0–100]
|
|
89746
89694
|
|
|
89747
|
-
* <P$ [score] = the combined partisan score used to compare plans *across* states
|
|
89748
|
-
* >P$ [score2] = the combined partisan score used to compare plans *within* a state, along with traditional districting principles
|
|
89749
|
-
|
|
89750
89695
|
*/
|
|
89751
89696
|
function scorePartisan(Vf, VfArray, options) {
|
|
89752
|
-
const
|
|
89697
|
+
const bAdvanced = options.advanced;
|
|
89753
89698
|
const bConstrained = options.constrained;
|
|
89754
89699
|
const shift = options.shift;
|
|
89755
89700
|
const N = VfArray.length;
|
|
89756
89701
|
const bestS = bestSeats(N, Vf);
|
|
89757
89702
|
const bestSf = bestSeatShare(bestS, N);
|
|
89758
|
-
const fptpS =
|
|
89759
|
-
//
|
|
89703
|
+
const fptpS = bAdvanced ? estFPTPSeats(VfArray) : undefined;
|
|
89704
|
+
// When not constrained, use the full probability distribution to calc metrics.
|
|
89760
89705
|
const range = bConstrained ? C.competitiveDistribution() : undefined;
|
|
89761
89706
|
const estS = estSeats(VfArray, range);
|
|
89762
89707
|
const estSf = estSeatShare(estS, N);
|
|
89763
|
-
const
|
|
89764
|
-
const
|
|
89765
|
-
const unearnedS = estUnearnedSeats(bestS, estS);
|
|
89766
|
-
const impactScore = scoreImpact(unearnedS, Vf, estSf, N);
|
|
89708
|
+
const deviation = estDeviation(estSf, bestSf);
|
|
89709
|
+
const proportionalityScore = scoreDeviation(deviation, Vf, estSf);
|
|
89767
89710
|
// Calculate additional alternate metrics for reference
|
|
89768
|
-
// NOTE - Use the uncompressed seat probability function
|
|
89769
89711
|
const dSVpoints = inferSVpoints(Vf, VfArray, shift);
|
|
89770
89712
|
const rSVpoints = invertSVPoints(dSVpoints);
|
|
89771
|
-
// const dSVpoints =
|
|
89772
|
-
const TOf =
|
|
89773
|
-
const Bs50 =
|
|
89713
|
+
// const dSVpoints = bAdvanced ? inferSVpoints(Vf, VfArray, shift) : undefined;
|
|
89714
|
+
const TOf = bAdvanced ? calcTurnoutBias(Vf, VfArray) : undefined;
|
|
89715
|
+
const Bs50 = bAdvanced ? estPartisanBias(dSVpoints, N) : undefined;
|
|
89774
89716
|
const Bs50f = (!(Bs50 === undefined)) ? U.trim(Bs50 / N) : undefined;
|
|
89775
|
-
const Bv50f =
|
|
89776
|
-
const rvPoints =
|
|
89777
|
-
const decl =
|
|
89778
|
-
const gSym =
|
|
89779
|
-
const EG =
|
|
89780
|
-
const BsGf =
|
|
89781
|
-
const prop =
|
|
89782
|
-
const mMs =
|
|
89783
|
-
const mMd =
|
|
89784
|
-
const LO =
|
|
89717
|
+
const Bv50f = bAdvanced ? estVotesBias(dSVpoints, N) : undefined;
|
|
89718
|
+
const rvPoints = bAdvanced ? keyRVpoints(VfArray) : undefined;
|
|
89719
|
+
const decl = bAdvanced ? calcDeclination(VfArray) : undefined;
|
|
89720
|
+
const gSym = bAdvanced ? calcGlobalSymmetry(dSVpoints, rSVpoints, Bs50f) : undefined;
|
|
89721
|
+
const EG = bAdvanced ? calcEfficiencyGap(Vf, estSf) : undefined;
|
|
89722
|
+
const BsGf = bAdvanced ? estGeometricSeatsBias(Vf, dSVpoints, rSVpoints) : undefined;
|
|
89723
|
+
const prop = bAdvanced ? calcDisproportionality(Vf, estSf) : undefined;
|
|
89724
|
+
const mMs = bAdvanced ? estMeanMedianDifference(VfArray, Vf) : undefined;
|
|
89725
|
+
const mMd = bAdvanced ? estMeanMedianDifference(VfArray) : undefined;
|
|
89726
|
+
const LO = bAdvanced ? calcLopsidedOutcomes(VfArray) : undefined;
|
|
89785
89727
|
// Calculate alternate responsiveness metrics for reference
|
|
89786
|
-
const bigR =
|
|
89787
|
-
const littleR =
|
|
89788
|
-
const MIR = (
|
|
89789
|
-
const rD = (!bConstrained ||
|
|
89790
|
-
const rDf =
|
|
89791
|
-
const gamma = (
|
|
89728
|
+
const bigR = bAdvanced ? calcBigR(Vf, estSf) : undefined;
|
|
89729
|
+
const littleR = bAdvanced ? estResponsiveness(Vf, dSVpoints) : undefined;
|
|
89730
|
+
const MIR = (bAdvanced && littleR) ? calcMinimalInverseResponsiveness(Vf, littleR) : undefined;
|
|
89731
|
+
const rD = (!bConstrained || bAdvanced) ? estResponsiveDistricts(VfArray) : undefined;
|
|
89732
|
+
const rDf = bAdvanced ? estResponsiveDistrictsShare(rD, N) : undefined;
|
|
89733
|
+
const gamma = (bAdvanced && littleR) ? calcGamma(Vf, estSf, littleR) : undefined;
|
|
89792
89734
|
const Cn = countCompetitiveDistricts(VfArray);
|
|
89793
89735
|
// NOTE - Cd by definition uses a *possibly* different (more narrow) probability
|
|
89794
89736
|
// distribution than Rd.
|
|
89795
89737
|
const cD = estCompetitiveDistricts(VfArray);
|
|
89796
|
-
// const cD = bConstrained ? estCompetitiveDistricts(VfArray) : rD as number;
|
|
89797
89738
|
const cDf = estCompetitiveDistrictsShare(cD, N);
|
|
89798
89739
|
const competitivenessScore = scoreCompetitiveness(cDf);
|
|
89799
|
-
// NOTE: Original version:
|
|
89800
|
-
// const Mrange = findMarginalDistricts(Vf, VfArray, N);
|
|
89801
|
-
// const Md = estMarginalCompetitiveDistricts(Mrange, VfArray);
|
|
89802
|
-
// const Mdf = estMarginalCompetitiveShare(Md, Mrange);
|
|
89803
|
-
// const competitivenessScore = scoreCompetitiveness(Mdf, cDf);
|
|
89804
89740
|
let biasScoring = {
|
|
89805
89741
|
bestS: bestS,
|
|
89806
89742
|
bestSf: bestSf,
|
|
89807
89743
|
estS: estS,
|
|
89808
89744
|
estSf: estSf,
|
|
89809
|
-
|
|
89810
|
-
score:
|
|
89811
|
-
};
|
|
89812
|
-
const impactScoring = {
|
|
89813
|
-
unearnedS: unearnedS,
|
|
89814
|
-
score: impactScore
|
|
89745
|
+
deviation: deviation,
|
|
89746
|
+
score: proportionalityScore
|
|
89815
89747
|
};
|
|
89816
89748
|
let competitiveScoring = {
|
|
89817
89749
|
cSimple: Cn,
|
|
89818
89750
|
cD: cD,
|
|
89819
89751
|
cDf: cDf,
|
|
89820
|
-
// mRange: Mrange,
|
|
89821
|
-
// mD: Md,
|
|
89822
|
-
// mDf: Mdf,
|
|
89823
89752
|
score: competitivenessScore
|
|
89824
89753
|
};
|
|
89825
|
-
if (
|
|
89754
|
+
if (bAdvanced) {
|
|
89826
89755
|
biasScoring.tOf = TOf;
|
|
89827
89756
|
biasScoring.fptpS = fptpS;
|
|
89828
89757
|
biasScoring.bS50 = Bs50f;
|
|
@@ -89843,47 +89772,36 @@ function scorePartisan(Vf, VfArray, options) {
|
|
|
89843
89772
|
competitiveScoring.rD = rD;
|
|
89844
89773
|
competitiveScoring.rDf = rDf;
|
|
89845
89774
|
}
|
|
89846
|
-
|
|
89847
|
-
|
|
89848
|
-
const
|
|
89849
|
-
const
|
|
89850
|
-
const cS = competitiveScoring.score;
|
|
89851
|
-
const acrossStatesPartisanScore = weightPartisan(bS, iS, cS, 0 /* AcrossStates */);
|
|
89852
|
-
const withinStatesPartisanScore = weightPartisan(bS, iS, cS, 1 /* WithinAState */);
|
|
89775
|
+
const DWins = VfArray.filter(x => x > 0.5);
|
|
89776
|
+
const RWins = VfArray.filter(x => x <= 0.5); // Ties credited to R's
|
|
89777
|
+
const averageDVf = (DWins.length > 0) ? U.avgArray(DWins) : undefined;
|
|
89778
|
+
const averageRVf = (RWins.length > 0) ? U.avgArray(RWins) : undefined;
|
|
89853
89779
|
const s = {
|
|
89854
89780
|
bias: biasScoring,
|
|
89855
|
-
|
|
89856
|
-
competitiveness: competitiveScoring,
|
|
89781
|
+
responsiveness: competitiveScoring,
|
|
89857
89782
|
dSVpoints: dSVpoints,
|
|
89858
89783
|
rSVpoints: rSVpoints,
|
|
89859
|
-
|
|
89860
|
-
|
|
89784
|
+
averageDVf: averageDVf,
|
|
89785
|
+
averageRVf: averageRVf,
|
|
89861
89786
|
details: {}
|
|
89862
89787
|
};
|
|
89863
89788
|
return s;
|
|
89864
89789
|
}
|
|
89865
89790
|
exports.scorePartisan = scorePartisan;
|
|
89866
|
-
function weightPartisan(bS, iS, cS, context) {
|
|
89867
|
-
const bW = C.biasWeight(context);
|
|
89868
|
-
const iW = C.impactWeight(context);
|
|
89869
|
-
const cW = C.competitivenessWeight(context);
|
|
89870
|
-
const score = Math.round(((bS * bW) + (iS * iW) + (cS * cW)) / (bW + iW + cW));
|
|
89871
|
-
return score;
|
|
89872
|
-
}
|
|
89873
|
-
exports.weightPartisan = weightPartisan;
|
|
89874
89791
|
function extraBonus(Vf) {
|
|
89875
|
-
const
|
|
89792
|
+
const over50Pct = (Vf > 0.5) ? (Vf - 0.5) : (0.5 - Vf);
|
|
89793
|
+
const okExtra = over50Pct * (C.winnerBonus() - 1.0);
|
|
89876
89794
|
return U.trim(okExtra);
|
|
89877
89795
|
}
|
|
89878
89796
|
exports.extraBonus = extraBonus;
|
|
89879
|
-
function
|
|
89797
|
+
function scoreDeviation(deviation, Vf, Sf) {
|
|
89880
89798
|
if (isAntimajoritarian(Vf, Sf)) {
|
|
89881
89799
|
return 0;
|
|
89882
89800
|
}
|
|
89883
89801
|
else {
|
|
89884
89802
|
// Adjust bias to incorporate an acceptable winner's bonus based on Vf
|
|
89885
89803
|
const extra = extraBonus(Vf);
|
|
89886
|
-
const adjusted =
|
|
89804
|
+
const adjusted = adjustDeviation(Vf, deviation, extra);
|
|
89887
89805
|
// Then normalize
|
|
89888
89806
|
const _normalizer = new normalize_1.Normalizer(adjusted);
|
|
89889
89807
|
const worst = C.biasRange()[C.BEG];
|
|
@@ -89897,15 +89815,24 @@ function scorebias(rawBias, Vf, Sf) {
|
|
|
89897
89815
|
return score;
|
|
89898
89816
|
}
|
|
89899
89817
|
}
|
|
89900
|
-
exports.
|
|
89901
|
-
// Adjust
|
|
89902
|
-
|
|
89903
|
-
|
|
89904
|
-
|
|
89905
|
-
|
|
89906
|
-
|
|
89818
|
+
exports.scoreDeviation = scoreDeviation;
|
|
89819
|
+
// Adjust deviation from proportionality to account for a winner's bonus
|
|
89820
|
+
// * If the bias is in the *same* direction as the statewide vote %, then
|
|
89821
|
+
// discount the bias by the winner's bonus (extra).
|
|
89822
|
+
// * But if the bias and statewide vote % go in opposite directions, leave the
|
|
89823
|
+
// bias unadjusted.
|
|
89824
|
+
function adjustDeviation(Vf, deviation, extra) {
|
|
89825
|
+
let adjusted = deviation;
|
|
89826
|
+
if ((Vf > 0.5) && (deviation < 0)) {
|
|
89827
|
+
adjusted = Math.min(deviation + extra, 0);
|
|
89828
|
+
}
|
|
89829
|
+
else if ((Vf < 0.5) && (deviation > 0)) {
|
|
89830
|
+
adjusted = Math.max(deviation - extra, 0);
|
|
89831
|
+
}
|
|
89832
|
+
return adjusted;
|
|
89907
89833
|
}
|
|
89908
|
-
exports.
|
|
89834
|
+
exports.adjustDeviation = adjustDeviation;
|
|
89835
|
+
// NOTE - Not used.
|
|
89909
89836
|
// Normalize unearned seats
|
|
89910
89837
|
function scoreImpact(rawUE, Vf, Sf, N) {
|
|
89911
89838
|
if (isAntimajoritarian(Vf, Sf)) {
|
|
@@ -89914,7 +89841,7 @@ function scoreImpact(rawUE, Vf, Sf, N) {
|
|
|
89914
89841
|
else {
|
|
89915
89842
|
// Adjust impact to incorporate an acceptable winner's bonus based on Vf
|
|
89916
89843
|
const extra = extraBonus(Vf);
|
|
89917
|
-
const adjustedBias =
|
|
89844
|
+
const adjustedBias = adjustDeviation(Vf, rawUE / N, extra);
|
|
89918
89845
|
const adjustedImpact = adjustedBias * N;
|
|
89919
89846
|
// Then normalize
|
|
89920
89847
|
const _normalizer = new normalize_1.Normalizer(adjustedImpact);
|
|
@@ -89937,9 +89864,8 @@ function isAntimajoritarian(Vf, Sf) {
|
|
|
89937
89864
|
return bDem || bRep;
|
|
89938
89865
|
}
|
|
89939
89866
|
exports.isAntimajoritarian = isAntimajoritarian;
|
|
89940
|
-
// COMPETITIVENESS - Revised 06/23/2020
|
|
89941
89867
|
// Normalize overall competitiveness - Raw values are in the range [0.0–1.0].
|
|
89942
|
-
// But the practical max is more like
|
|
89868
|
+
// But the practical max is more like 3/4's, so unitize that range to [0.0–1.0].
|
|
89943
89869
|
// Then scale the values to [0–100].
|
|
89944
89870
|
function scoreCompetitiveness(Cdf) {
|
|
89945
89871
|
const _normalizer = new normalize_1.Normalizer(Cdf);
|
|
@@ -90032,8 +89958,6 @@ function inferSVpoints(Vf, VfArray, shift, range) {
|
|
|
90032
89958
|
const shiftedVPI = shiftDistricts(Vf, VfArray, shiftedVf, shift);
|
|
90033
89959
|
const shiftedSf = estSeats(shiftedVPI, range) / nDistricts;
|
|
90034
89960
|
SVpoints.push({ v: shiftedVf, s: shiftedSf });
|
|
90035
|
-
// TODO - Why can't I trim these? Why does that only break the Hypotheticals?!?
|
|
90036
|
-
// SVpoints.push({v: U.trim(Number(shiftedVf)), s: shiftedSf});
|
|
90037
89961
|
}
|
|
90038
89962
|
return SVpoints;
|
|
90039
89963
|
}
|
|
@@ -90118,11 +90042,12 @@ function estFPTPSeats(VfArray) {
|
|
|
90118
90042
|
}));
|
|
90119
90043
|
}
|
|
90120
90044
|
exports.estFPTPSeats = estFPTPSeats;
|
|
90121
|
-
// B% - The
|
|
90122
|
-
function
|
|
90045
|
+
// B% - The deviation from proportionality calculated as ^S% — S%
|
|
90046
|
+
function estDeviation(estSf, bestSf) {
|
|
90123
90047
|
return U.trim(bestSf - estSf);
|
|
90124
90048
|
}
|
|
90125
|
-
exports.
|
|
90049
|
+
exports.estDeviation = estDeviation;
|
|
90050
|
+
// NOTE - Not used.
|
|
90126
90051
|
// UE# - The estimated # of unearned seats
|
|
90127
90052
|
// UE_# from http://bit.ly/2Fcuf4q
|
|
90128
90053
|
function estUnearnedSeats(proportional, probable) {
|
|
@@ -90227,11 +90152,9 @@ function estCompetitiveDistricts(VfArray, bCompress = false) {
|
|
|
90227
90152
|
return U.trim(U.sumArray(VfArray.map(v => estDistrictCompetitiveness(v, bCompress))));
|
|
90228
90153
|
}
|
|
90229
90154
|
exports.estCompetitiveDistricts = estCompetitiveDistricts;
|
|
90230
|
-
// COMPETITIVENESS - Modified 06/22/2020
|
|
90231
|
-
// Re-scale a Democratic vote share to the competitive range (e.g., 45–55%).
|
|
90232
90155
|
function estDistrictCompetitiveness(Vf, bCompress = false) {
|
|
90233
90156
|
const _normalizer = new normalize_1.Normalizer(Vf);
|
|
90234
|
-
// The end points of
|
|
90157
|
+
// The end points of the probability distribution
|
|
90235
90158
|
// NOTE - These aren't the points where races start or stop being contested,
|
|
90236
90159
|
// just the end points of a distribution that yields the desired behavior
|
|
90237
90160
|
// in the typical competitive range [45-55%].
|
|
@@ -90456,9 +90379,6 @@ function keyRVpoints(VfArray) {
|
|
|
90456
90379
|
const nDistricts = VfArray.length;
|
|
90457
90380
|
const estS = estSeats(VfArray);
|
|
90458
90381
|
const Sb = estSeatShare(estS, nDistricts);
|
|
90459
|
-
// TODO - Understand why the corresponding V to Sb is always @ 0.5.
|
|
90460
|
-
// John Nagle: "This is the dividing vote for party A vs party B wins defined
|
|
90461
|
-
// by Warrington. My modification just puts fractions of districts to the each side."
|
|
90462
90382
|
const Rb = Sb / 2;
|
|
90463
90383
|
const Ra = (1 + Sb) / 2;
|
|
90464
90384
|
const Vb = 1.0 - (U.sumArray(VfArray.map(v => estSeatProbability(v) * v))) / estS;
|
|
@@ -90598,23 +90518,19 @@ function calcGamma(Vf, Sf, r) {
|
|
|
90598
90518
|
exports.calcGamma = calcGamma;
|
|
90599
90519
|
// HELPERS
|
|
90600
90520
|
function printPartisanScorecardHeader() {
|
|
90601
|
-
console.log('XX, Name, N, V%, ^S#, S#, B%, B$,
|
|
90521
|
+
console.log('XX, Name, N, V%, ^S#, S#, B%, B$, C#, Cd, Cdf, C$');
|
|
90602
90522
|
}
|
|
90603
90523
|
exports.printPartisanScorecardHeader = printPartisanScorecardHeader;
|
|
90604
90524
|
function printPartisanScorecardRow(xx, name, N, Vf, s) {
|
|
90605
|
-
console.log('%s, %s, %i, %f, %i, %f, %f, %i, %
|
|
90525
|
+
console.log('%s, %s, %i, %f, %i, %f, %f, %i, %i, %f, %f, %i', xx, // 1
|
|
90606
90526
|
name, // 2
|
|
90607
90527
|
N, // 3
|
|
90608
90528
|
Vf, // 4
|
|
90609
90529
|
s.bias.bestS, // 5
|
|
90610
90530
|
s.bias.estS, // 6
|
|
90611
|
-
s.bias.
|
|
90612
|
-
s.bias.score, s.
|
|
90613
|
-
s.
|
|
90614
|
-
s.competitiveness.cD, s.competitiveness.cDf,
|
|
90615
|
-
// s.competitiveness.mD,
|
|
90616
|
-
s.competitiveness.score, s.score // 15
|
|
90617
|
-
);
|
|
90531
|
+
s.bias.deviation, // 7
|
|
90532
|
+
s.bias.score, s.responsiveness.cSimple, // 9
|
|
90533
|
+
s.responsiveness.cD, s.responsiveness.cDf, s.responsiveness.score);
|
|
90618
90534
|
}
|
|
90619
90535
|
exports.printPartisanScorecardRow = printPartisanScorecardRow;
|
|
90620
90536
|
// Generate partisan details (Table 1)
|
|
@@ -90623,12 +90539,12 @@ function printPartisanDetailsHeader() {
|
|
|
90623
90539
|
}
|
|
90624
90540
|
exports.printPartisanDetailsHeader = printPartisanDetailsHeader;
|
|
90625
90541
|
function printPartisanDetailsRow(xx, name, N, Vf, s) {
|
|
90626
|
-
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.
|
|
90542
|
+
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
|
|
90627
90543
|
s.bias.bS50, s.bias.bV50, s.bias.decl, s.bias.gSym, s.bias.eG, s.bias.bSV, // Beta
|
|
90628
90544
|
s.bias.prop, // PR
|
|
90629
|
-
s.bias.mMs, s.bias.tOf, s.bias.mMd, s.bias.lO, s.
|
|
90630
|
-
s.
|
|
90631
|
-
s.
|
|
90545
|
+
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
|
|
90546
|
+
s.responsiveness.cD, // COMPETITIVENESS - Temporary to confirm new calc
|
|
90547
|
+
s.responsiveness.cDf);
|
|
90632
90548
|
}
|
|
90633
90549
|
exports.printPartisanDetailsRow = printPartisanDetailsRow;
|
|
90634
90550
|
|
|
@@ -90655,7 +90571,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
90655
90571
|
return result;
|
|
90656
90572
|
};
|
|
90657
90573
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90658
|
-
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
90659
90574
|
const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
90660
90575
|
const compact_1 = __webpack_require__(/*! ./compact */ "./src/compact.ts");
|
|
90661
90576
|
const cohesive_1 = __webpack_require__(/*! ./cohesive */ "./src/cohesive.ts");
|
|
@@ -90671,7 +90586,8 @@ function scorePlan(p, overridesJSON) {
|
|
|
90671
90586
|
const cS = {
|
|
90672
90587
|
score: compactnessScore,
|
|
90673
90588
|
reock: reockM,
|
|
90674
|
-
polsby: polsbyM
|
|
90589
|
+
polsby: polsbyM,
|
|
90590
|
+
details: {}
|
|
90675
90591
|
};
|
|
90676
90592
|
// Splitting
|
|
90677
90593
|
const CxD = p.splittingProfile.countyPopByDistrict;
|
|
@@ -90683,40 +90599,26 @@ function scorePlan(p, overridesJSON) {
|
|
|
90683
90599
|
const sS = {
|
|
90684
90600
|
score: splittingScore,
|
|
90685
90601
|
county: countyM,
|
|
90686
|
-
district: districtM
|
|
90602
|
+
district: districtM,
|
|
90603
|
+
details: {}
|
|
90687
90604
|
};
|
|
90688
90605
|
// Population deviation
|
|
90689
90606
|
const pdS = equal_1.doPopulationDeviation(p.populationProfile.totalPopByDistrict, p.populationProfile.targetSize, p.legislativeDistricts);
|
|
90690
|
-
//
|
|
90691
|
-
const tpScore = weightTradtionalPrinciples(cS.score, sS.score, pdS.normalized, 1 /* WithinAState */);
|
|
90692
|
-
// Populate the "best" traditional principles scorecard
|
|
90693
|
-
const tpS = {
|
|
90694
|
-
score: tpScore,
|
|
90695
|
-
compactness: cS,
|
|
90696
|
-
splitting: sS,
|
|
90697
|
-
populationDeviation: pdS,
|
|
90698
|
-
details: {}
|
|
90699
|
-
};
|
|
90700
|
-
// PARTISAN ("fair") subcategories - bias, impact, & competitiveness (plus lots of supporting measures)
|
|
90607
|
+
// Partisan - bias & responsiveness
|
|
90701
90608
|
const options = {
|
|
90702
|
-
|
|
90609
|
+
advanced: true,
|
|
90703
90610
|
constrained: false,
|
|
90704
90611
|
shift: 0 /* Proportional */
|
|
90705
90612
|
};
|
|
90706
90613
|
const pS = partisan_1.scorePartisan(p.partisanProfile.statewideVf, p.partisanProfile.vfArray, options);
|
|
90707
|
-
// Combine the partisan/partisan & traditional principles/best scores into an overall
|
|
90708
|
-
// score for comparing plans w/in a state
|
|
90709
|
-
let score = weightOverall(pS.score2, tpS.score, 1 /* WithinAState */);
|
|
90710
90614
|
const mS = minority_1.evalMinorityOpportunity(p);
|
|
90711
|
-
//
|
|
90712
|
-
const bonus = C.minorityBonus() * (mS.score / 100);
|
|
90713
|
-
score = mixinMinorityBonus(score, bonus);
|
|
90714
|
-
// Roll up an overall scorecard
|
|
90615
|
+
// Combine the pieces into a scorecard
|
|
90715
90616
|
const scorecard = {
|
|
90716
90617
|
partisan: pS,
|
|
90717
90618
|
minority: mS,
|
|
90718
|
-
|
|
90719
|
-
|
|
90619
|
+
compactness: cS,
|
|
90620
|
+
splitting: sS,
|
|
90621
|
+
populationDeviation: pdS,
|
|
90720
90622
|
details: {}
|
|
90721
90623
|
};
|
|
90722
90624
|
return scorecard;
|
|
@@ -90737,44 +90639,20 @@ function weightSplitting(csS, dsS) {
|
|
|
90737
90639
|
return score;
|
|
90738
90640
|
}
|
|
90739
90641
|
exports.weightSplitting = weightSplitting;
|
|
90740
|
-
function weightTradtionalPrinciples(cS, sS, pdS, context) {
|
|
90741
|
-
const cW = C.compactnessWeight(context);
|
|
90742
|
-
const sW = C.splittingWeight(context);
|
|
90743
|
-
const pdW = C.popdevWeight(context);
|
|
90744
|
-
const score = Math.round(((cS * cW) + (sS * sW) + (pdS * pdW)) / (cW + sW + pdW));
|
|
90745
|
-
return score;
|
|
90746
|
-
}
|
|
90747
|
-
exports.weightTradtionalPrinciples = weightTradtionalPrinciples;
|
|
90748
|
-
function weightOverall(pS, tpS, context) {
|
|
90749
|
-
const pW = C.partisanWeight(context);
|
|
90750
|
-
const tpW = C.traditionalPrinciplesWeight(context);
|
|
90751
|
-
const score = Math.round(((pS * pW) + (tpS * tpW)) / (pW + tpW));
|
|
90752
|
-
return score;
|
|
90753
|
-
}
|
|
90754
|
-
exports.weightOverall = weightOverall;
|
|
90755
|
-
function mixinMinorityBonus(score, minorityBonus) {
|
|
90756
|
-
const modifiedScore = Math.min(score + minorityBonus, S.NORMALIZED_RANGE);
|
|
90757
|
-
return modifiedScore;
|
|
90758
|
-
}
|
|
90759
|
-
exports.mixinMinorityBonus = mixinMinorityBonus;
|
|
90760
90642
|
function printScorecardHeader() {
|
|
90761
|
-
console.log('XX, Name, N, V%, ^S#, S#, B%, B$,
|
|
90643
|
+
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$');
|
|
90762
90644
|
}
|
|
90763
90645
|
exports.printScorecardHeader = printScorecardHeader;
|
|
90764
90646
|
function printScorecardRow(xx, name, N, Vf, s) {
|
|
90765
|
-
console.log('%s, %s, %i, %f, %i, %f, %f, %i, %
|
|
90647
|
+
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
|
|
90766
90648
|
name, // 2
|
|
90767
90649
|
N, // 3
|
|
90768
90650
|
Vf, // 4
|
|
90769
90651
|
s.partisan.bias.bestS, // 5
|
|
90770
90652
|
s.partisan.bias.estS, // 6
|
|
90771
|
-
s.partisan.bias.
|
|
90772
|
-
s.partisan.bias.score, s.partisan.
|
|
90773
|
-
s.partisan.
|
|
90774
|
-
s.partisan.competitiveness.cD, s.partisan.competitiveness.cDf,
|
|
90775
|
-
// s.partisan.competitiveness.mD,
|
|
90776
|
-
s.partisan.competitiveness.score, s.partisan.score2, // 15
|
|
90777
|
-
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);
|
|
90653
|
+
s.partisan.bias.deviation, // 7
|
|
90654
|
+
s.partisan.bias.score, s.partisan.responsiveness.cSimple, // 9
|
|
90655
|
+
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);
|
|
90778
90656
|
}
|
|
90779
90657
|
exports.printScorecardRow = printScorecardRow;
|
|
90780
90658
|
|
|
@@ -102923,10 +102801,10 @@ class AnalyticsSession {
|
|
|
102923
102801
|
const scorecard = this._scorecard;
|
|
102924
102802
|
const r = {
|
|
102925
102803
|
proportionality: scorecard.partisan.bias.score,
|
|
102926
|
-
competitiveness: scorecard.partisan.
|
|
102804
|
+
competitiveness: scorecard.partisan.responsiveness.score,
|
|
102927
102805
|
minorityRights: scorecard.minority.score,
|
|
102928
|
-
compactness: scorecard.
|
|
102929
|
-
splitting: scorecard.
|
|
102806
|
+
compactness: scorecard.compactness.score,
|
|
102807
|
+
splitting: scorecard.splitting.score
|
|
102930
102808
|
};
|
|
102931
102809
|
return r;
|
|
102932
102810
|
}
|
|
@@ -104424,7 +104302,7 @@ function doAnalyzePostProcessing(s, bLog = false) {
|
|
|
104424
104302
|
// Just populate the normalized population deviation score in the test
|
|
104425
104303
|
const scorecard = s._scorecard;
|
|
104426
104304
|
let popDev = s.getTest(4 /* PopulationDeviation */);
|
|
104427
|
-
popDev['normalizedScore'] = scorecard.
|
|
104305
|
+
popDev['normalizedScore'] = scorecard.populationDeviation.normalized;
|
|
104428
104306
|
const datasets = {
|
|
104429
104307
|
shapes: S.SHAPES,
|
|
104430
104308
|
census: U.deepCopy(s.config['descriptions']['CENSUS']),
|
|
@@ -104433,12 +104311,12 @@ function doAnalyzePostProcessing(s, bLog = false) {
|
|
|
104433
104311
|
};
|
|
104434
104312
|
scorecard.partisan.details['election'] = datasets.election;
|
|
104435
104313
|
scorecard.minority.details['vap'] = datasets.vap;
|
|
104436
|
-
scorecard.
|
|
104437
|
-
scorecard.
|
|
104314
|
+
scorecard.details['shapes'] = datasets.shapes;
|
|
104315
|
+
scorecard.details['census'] = datasets.census;
|
|
104438
104316
|
const simpleSplits = s.getTest(5 /* UnexpectedCountySplits */);
|
|
104439
|
-
scorecard.
|
|
104440
|
-
scorecard.
|
|
104441
|
-
scorecard.
|
|
104317
|
+
scorecard.compactness.details['unexpectedAffected'] = simpleSplits['score'];
|
|
104318
|
+
scorecard.compactness.details['nSplits'] = simpleSplits['details']['nSplits'];
|
|
104319
|
+
scorecard.compactness.details['countiesSplitUnexpectedly'] = U.deepCopy(simpleSplits['details']['countiesSplitUnexpectedly']);
|
|
104442
104320
|
// NOTE - Add split precincts in dra-client directly
|
|
104443
104321
|
// Derive secondary tests
|
|
104444
104322
|
analyze_1.doDeriveSecondaryTests(s, bLog);
|
|
@@ -104583,10 +104461,10 @@ function scorePlan(s, p, bLog = false, overridesJSON) {
|
|
|
104583
104461
|
// doHasEqualPopulations() to use later.This is preserving the old calling sequence.
|
|
104584
104462
|
let test = s.getTest(4 /* PopulationDeviation */);
|
|
104585
104463
|
// Get the raw population deviation
|
|
104586
|
-
const popDev = scorecard.
|
|
104464
|
+
const popDev = scorecard.populationDeviation.raw;
|
|
104587
104465
|
// Populate the test entry
|
|
104588
104466
|
test['score'] = popDev;
|
|
104589
|
-
test['details'] = { 'maxDeviation': scorecard.
|
|
104467
|
+
test['details'] = { 'maxDeviation': scorecard.populationDeviation.notes['maxDeviation'] };
|
|
104590
104468
|
// Populate the N+1 summary "district" in district.statistics
|
|
104591
104469
|
let totalPop = s.districts.statistics[D.DistrictField.TotalPop];
|
|
104592
104470
|
let popDevPct = s.districts.statistics[D.DistrictField.PopDevPct];
|