@dra2020/district-analytics 3.1.0 → 4.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 +954 -453
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +138 -69
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/score.d.ts +1 -1
- package/dist/src/types.d.ts +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -86,10 +86,10 @@
|
|
|
86
86
|
/************************************************************************/
|
|
87
87
|
/******/ ({
|
|
88
88
|
|
|
89
|
-
/***/ "
|
|
90
|
-
|
|
91
|
-
!***
|
|
92
|
-
|
|
89
|
+
/***/ "./node_modules/@dra2020/dra-score/dist/dra-score.bundle.js":
|
|
90
|
+
/*!******************************************************************!*\
|
|
91
|
+
!*** ./node_modules/@dra2020/dra-score/dist/dra-score.bundle.js ***!
|
|
92
|
+
\******************************************************************/
|
|
93
93
|
/*! no static exports found */
|
|
94
94
|
/***/ (function(module, exports, __webpack_require__) {
|
|
95
95
|
|
|
@@ -89264,13 +89264,24 @@ module.exports = g;
|
|
|
89264
89264
|
//
|
|
89265
89265
|
// THE NODE PACKAGE API
|
|
89266
89266
|
//
|
|
89267
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
89268
|
+
if (mod && mod.__esModule) return mod;
|
|
89269
|
+
var result = {};
|
|
89270
|
+
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
89271
|
+
result["default"] = mod;
|
|
89272
|
+
return result;
|
|
89273
|
+
};
|
|
89267
89274
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
89275
|
+
const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
89268
89276
|
const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
|
|
89269
89277
|
class Scorer {
|
|
89270
89278
|
constructor() {
|
|
89271
89279
|
}
|
|
89272
|
-
score(
|
|
89273
|
-
return score_1.scorePlan(
|
|
89280
|
+
score(profile, overridesJSON) {
|
|
89281
|
+
return score_1.scorePlan(profile, overridesJSON);
|
|
89282
|
+
}
|
|
89283
|
+
populationDeviationThreshold(bLegislative) {
|
|
89284
|
+
return C.popdevThreshold(bLegislative);
|
|
89274
89285
|
}
|
|
89275
89286
|
}
|
|
89276
89287
|
exports.Scorer = Scorer;
|
|
@@ -89300,27 +89311,33 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
89300
89311
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
89301
89312
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
89302
89313
|
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
89314
|
+
const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
89303
89315
|
const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
|
|
89304
89316
|
function scoreCountySplitting(rawValue, nCounties, nDistricts) {
|
|
89305
89317
|
const _normalizer = new normalize_1.Normalizer(rawValue);
|
|
89306
|
-
const
|
|
89307
|
-
|
|
89308
|
-
_normalizer.
|
|
89318
|
+
const best = C.countySplittingRange()[C.BEG];
|
|
89319
|
+
const worst = countySplitThreshold(nCounties, nDistricts);
|
|
89320
|
+
_normalizer.clip(best, worst);
|
|
89321
|
+
_normalizer.unitize(best, worst);
|
|
89309
89322
|
_normalizer.invert();
|
|
89310
89323
|
_normalizer.rescale();
|
|
89311
89324
|
return _normalizer.normalizedNum;
|
|
89312
89325
|
}
|
|
89313
89326
|
exports.scoreCountySplitting = scoreCountySplitting;
|
|
89314
89327
|
function countySplitThreshold(nCounties, nDistricts) {
|
|
89315
|
-
const
|
|
89316
|
-
const
|
|
89328
|
+
const m = C.allowableSplitsMultiplier();
|
|
89329
|
+
const worst = C.countySplittingRange()[1];
|
|
89330
|
+
const nAllowableSplits = Math.min(m * (nDistricts - 1));
|
|
89331
|
+
const threshold = ((nAllowableSplits * worst) + ((nCounties - nAllowableSplits) * 1.0)) / nCounties;
|
|
89317
89332
|
return threshold;
|
|
89318
89333
|
}
|
|
89319
89334
|
exports.countySplitThreshold = countySplitThreshold;
|
|
89320
89335
|
function scoreDistrictSplitting(rawValue) {
|
|
89321
89336
|
const _normalizer = new normalize_1.Normalizer(rawValue);
|
|
89322
|
-
|
|
89323
|
-
|
|
89337
|
+
const best = C.districtSplittingRange()[C.BEG];
|
|
89338
|
+
const worst = C.districtSplittingRange()[C.END];
|
|
89339
|
+
_normalizer.clip(best, worst);
|
|
89340
|
+
_normalizer.unitize(best, worst);
|
|
89324
89341
|
_normalizer.invert();
|
|
89325
89342
|
_normalizer.rescale();
|
|
89326
89343
|
return _normalizer.normalizedNum;
|
|
@@ -89593,7 +89610,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
89593
89610
|
};
|
|
89594
89611
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
89595
89612
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
89596
|
-
const
|
|
89613
|
+
const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
89597
89614
|
const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
|
|
89598
89615
|
// Migrated from district-analytics:
|
|
89599
89616
|
// Measures of compactness compare district shapes to various ideally compact
|
|
@@ -89715,8 +89732,10 @@ function calcReock(area, diameter) {
|
|
|
89715
89732
|
exports.calcReock = calcReock;
|
|
89716
89733
|
function scoreReock(rawValue) {
|
|
89717
89734
|
const _normalizer = new normalize_1.Normalizer(rawValue);
|
|
89718
|
-
|
|
89719
|
-
|
|
89735
|
+
const worst = C.reockRange()[C.BEG];
|
|
89736
|
+
const best = C.reockRange()[C.END];
|
|
89737
|
+
_normalizer.clip(worst, best);
|
|
89738
|
+
_normalizer.unitize(worst, best);
|
|
89720
89739
|
_normalizer.rescale();
|
|
89721
89740
|
return _normalizer.normalizedNum;
|
|
89722
89741
|
}
|
|
@@ -89734,12 +89753,12 @@ function doPolsbyPopper(g, bLog = false) {
|
|
|
89734
89753
|
if (districtProps) {
|
|
89735
89754
|
let a = districtProps[0 /* Area */];
|
|
89736
89755
|
let p = districtProps[1 /* Perimeter */];
|
|
89737
|
-
let
|
|
89756
|
+
let polsby = calcPolsbyPopper(a, p);
|
|
89738
89757
|
// Save each district score
|
|
89739
|
-
scores.push(
|
|
89758
|
+
scores.push(polsby);
|
|
89740
89759
|
// Echo the results by district
|
|
89741
89760
|
if (bLog)
|
|
89742
|
-
console.log("Polsby-Popper for district", districtID, "=",
|
|
89761
|
+
console.log("Polsby-Popper for district", districtID, "=", polsby);
|
|
89743
89762
|
}
|
|
89744
89763
|
else {
|
|
89745
89764
|
console.log("No shape or no geopropertes in Polsby-Popper ...");
|
|
@@ -89762,14 +89781,228 @@ function calcPolsbyPopper(area, perimeter) {
|
|
|
89762
89781
|
exports.calcPolsbyPopper = calcPolsbyPopper;
|
|
89763
89782
|
function scorePolsbyPopper(rawValue) {
|
|
89764
89783
|
const _normalizer = new normalize_1.Normalizer(rawValue);
|
|
89765
|
-
|
|
89766
|
-
|
|
89784
|
+
const worst = C.polsbyRange()[C.BEG];
|
|
89785
|
+
const best = C.polsbyRange()[C.END];
|
|
89786
|
+
_normalizer.clip(worst, best);
|
|
89787
|
+
_normalizer.unitize(worst, best);
|
|
89767
89788
|
_normalizer.rescale();
|
|
89768
89789
|
return _normalizer.normalizedNum;
|
|
89769
89790
|
}
|
|
89770
89791
|
exports.scorePolsbyPopper = scorePolsbyPopper;
|
|
89771
89792
|
|
|
89772
89793
|
|
|
89794
|
+
/***/ }),
|
|
89795
|
+
|
|
89796
|
+
/***/ "./src/config.json":
|
|
89797
|
+
/*!*************************!*\
|
|
89798
|
+
!*** ./src/config.json ***!
|
|
89799
|
+
\*************************/
|
|
89800
|
+
/*! exports provided: partisan, minority, traditionalPrinciples, default */
|
|
89801
|
+
/***/ (function(module) {
|
|
89802
|
+
|
|
89803
|
+
module.exports = JSON.parse("{\"partisan\":{\"bias\":{\"range\":[0,0.2],\"weight\":[50,80]},\"impact\":{\"weight\":[50,0],\"threshold\":4},\"competitiveness\":{\"overall\":{\"range\":[0,0.67],\"weight\":25},\"marginal\":{\"range\":[0,0.85],\"weight\":75},\"range\":[0.45,0.55],\"distribution\":[0.25,0.75],\"weight\":[0,20]},\"weight\":[100,80]},\"minority\":{\"range\":[0.37,0.5],\"distribution\":[0.25,0.75],\"shift\":0.12,\"bonus\":20},\"traditionalPrinciples\":{\"compactness\":{\"reock\":{\"range\":[0.25,0.5],\"weight\":50},\"polsby\":{\"range\":[0.1,0.5],\"weight\":50},\"weight\":[0,50]},\"splitting\":{\"county\":{\"range\":[1,1.71],\"allowableSplitsMultiplier\":1.5,\"weight\":50},\"district\":{\"range\":[1,1.5],\"weight\":50},\"weight\":[0,50]},\"popdev\":{\"range\":[[0.0075,0.002],[0.1,-1]],\"weight\":[0,0]},\"weight\":[0,20]}}");
|
|
89804
|
+
|
|
89805
|
+
/***/ }),
|
|
89806
|
+
|
|
89807
|
+
/***/ "./src/config.ts":
|
|
89808
|
+
/*!***********************!*\
|
|
89809
|
+
!*** ./src/config.ts ***!
|
|
89810
|
+
\***********************/
|
|
89811
|
+
/*! no static exports found */
|
|
89812
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
89813
|
+
|
|
89814
|
+
"use strict";
|
|
89815
|
+
|
|
89816
|
+
//
|
|
89817
|
+
// THRESHOLDS, SCALES, AND WEIGHTS FOR SCORING MODEL
|
|
89818
|
+
//
|
|
89819
|
+
// * A layer of functions over config.json, so that they can be overridden dynamically (TODO)
|
|
89820
|
+
//
|
|
89821
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
89822
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
89823
|
+
};
|
|
89824
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
89825
|
+
// Defaults
|
|
89826
|
+
const config_json_1 = __importDefault(__webpack_require__(/*! ./config.json */ "./src/config.json"));
|
|
89827
|
+
exports.BEG = 0;
|
|
89828
|
+
exports.END = 1;
|
|
89829
|
+
// PARTISAN
|
|
89830
|
+
function biasWeight(context, overridesJSON) {
|
|
89831
|
+
const bW = config_json_1.default.partisan.bias.weight[context];
|
|
89832
|
+
return bW;
|
|
89833
|
+
}
|
|
89834
|
+
exports.biasWeight = biasWeight;
|
|
89835
|
+
function biasRange(overridesJSON) {
|
|
89836
|
+
const range = config_json_1.default.partisan.bias.range;
|
|
89837
|
+
return range;
|
|
89838
|
+
}
|
|
89839
|
+
exports.biasRange = biasRange;
|
|
89840
|
+
function impactWeight(context, overridesJSON) {
|
|
89841
|
+
const iW = config_json_1.default.partisan.impact.weight[context];
|
|
89842
|
+
return iW;
|
|
89843
|
+
}
|
|
89844
|
+
exports.impactWeight = impactWeight;
|
|
89845
|
+
// The maximum # of unearned seats that scores positively
|
|
89846
|
+
function unearnedThreshold(overridesJSON) {
|
|
89847
|
+
const threshold = config_json_1.default.partisan.impact.threshold;
|
|
89848
|
+
return threshold;
|
|
89849
|
+
}
|
|
89850
|
+
exports.unearnedThreshold = unearnedThreshold;
|
|
89851
|
+
function competitivenessWeight(context, overridesJSON) {
|
|
89852
|
+
const cW = config_json_1.default.partisan.competitiveness.weight[context];
|
|
89853
|
+
return cW;
|
|
89854
|
+
}
|
|
89855
|
+
exports.competitivenessWeight = competitivenessWeight;
|
|
89856
|
+
function competitiveRange(overridesJSON) {
|
|
89857
|
+
const range = config_json_1.default.partisan.competitiveness.range;
|
|
89858
|
+
return range;
|
|
89859
|
+
}
|
|
89860
|
+
exports.competitiveRange = competitiveRange;
|
|
89861
|
+
function competitiveDistribution(overridesJSON) {
|
|
89862
|
+
const dist = config_json_1.default.partisan.competitiveness.distribution;
|
|
89863
|
+
return dist;
|
|
89864
|
+
}
|
|
89865
|
+
exports.competitiveDistribution = competitiveDistribution;
|
|
89866
|
+
function overallCompetitivenessRange(overridesJSON) {
|
|
89867
|
+
const range = config_json_1.default.partisan.competitiveness.overall.range;
|
|
89868
|
+
return range;
|
|
89869
|
+
}
|
|
89870
|
+
exports.overallCompetitivenessRange = overallCompetitivenessRange;
|
|
89871
|
+
function marginalCompetitivenessRange(overridesJSON) {
|
|
89872
|
+
const range = config_json_1.default.partisan.competitiveness.marginal.range;
|
|
89873
|
+
return range;
|
|
89874
|
+
}
|
|
89875
|
+
exports.marginalCompetitivenessRange = marginalCompetitivenessRange;
|
|
89876
|
+
function marginalCompetitivenessWeight(overridesJSON) {
|
|
89877
|
+
const mcW = config_json_1.default.partisan.competitiveness.marginal.weight;
|
|
89878
|
+
return mcW;
|
|
89879
|
+
}
|
|
89880
|
+
exports.marginalCompetitivenessWeight = marginalCompetitivenessWeight;
|
|
89881
|
+
function overallCompetitivenessWeight(overridesJSON) {
|
|
89882
|
+
const ocW = config_json_1.default.partisan.competitiveness.overall.weight;
|
|
89883
|
+
return ocW;
|
|
89884
|
+
}
|
|
89885
|
+
exports.overallCompetitivenessWeight = overallCompetitivenessWeight;
|
|
89886
|
+
// MINORITY
|
|
89887
|
+
function minorityOpportunityRange(overridesJSON) {
|
|
89888
|
+
const range = config_json_1.default.minority.range;
|
|
89889
|
+
return range;
|
|
89890
|
+
}
|
|
89891
|
+
exports.minorityOpportunityRange = minorityOpportunityRange;
|
|
89892
|
+
function minorityOpportunityDistribution(overridesJSON) {
|
|
89893
|
+
const dist = config_json_1.default.minority.distribution;
|
|
89894
|
+
return dist;
|
|
89895
|
+
}
|
|
89896
|
+
exports.minorityOpportunityDistribution = minorityOpportunityDistribution;
|
|
89897
|
+
function minorityShift(overridesJSON) {
|
|
89898
|
+
const shift = config_json_1.default.minority.shift;
|
|
89899
|
+
return shift;
|
|
89900
|
+
}
|
|
89901
|
+
exports.minorityShift = minorityShift;
|
|
89902
|
+
function opportunityDistrictBonus(overridesJSON) {
|
|
89903
|
+
const bonus = config_json_1.default.minority.bonus;
|
|
89904
|
+
return bonus;
|
|
89905
|
+
}
|
|
89906
|
+
exports.opportunityDistrictBonus = opportunityDistrictBonus;
|
|
89907
|
+
function minorityBonus(overridesJSON) {
|
|
89908
|
+
const bonus = config_json_1.default.minority.bonus;
|
|
89909
|
+
return bonus;
|
|
89910
|
+
}
|
|
89911
|
+
exports.minorityBonus = minorityBonus;
|
|
89912
|
+
// TRADITIONAL DISTRICTING PRINCIPLES
|
|
89913
|
+
function compactnessWeight(context, overridesJSON) {
|
|
89914
|
+
const cW = config_json_1.default.traditionalPrinciples.compactness.weight[context];
|
|
89915
|
+
return cW;
|
|
89916
|
+
}
|
|
89917
|
+
exports.compactnessWeight = compactnessWeight;
|
|
89918
|
+
function reockWeight(overridesJSON) {
|
|
89919
|
+
const rW = config_json_1.default.traditionalPrinciples.compactness.reock.weight;
|
|
89920
|
+
return rW;
|
|
89921
|
+
}
|
|
89922
|
+
exports.reockWeight = reockWeight;
|
|
89923
|
+
function reockRange(overridesJSON) {
|
|
89924
|
+
const range = config_json_1.default.traditionalPrinciples.compactness.reock.range;
|
|
89925
|
+
return range;
|
|
89926
|
+
}
|
|
89927
|
+
exports.reockRange = reockRange;
|
|
89928
|
+
function polsbyWeight(overridesJSON) {
|
|
89929
|
+
const ppW = config_json_1.default.traditionalPrinciples.compactness.polsby.weight;
|
|
89930
|
+
return ppW;
|
|
89931
|
+
}
|
|
89932
|
+
exports.polsbyWeight = polsbyWeight;
|
|
89933
|
+
function polsbyRange(overridesJSON) {
|
|
89934
|
+
const range = config_json_1.default.traditionalPrinciples.compactness.polsby.range;
|
|
89935
|
+
return range;
|
|
89936
|
+
}
|
|
89937
|
+
exports.polsbyRange = polsbyRange;
|
|
89938
|
+
function splittingWeight(context, overridesJSON) {
|
|
89939
|
+
const sW = config_json_1.default.traditionalPrinciples.splitting.weight[context];
|
|
89940
|
+
return sW;
|
|
89941
|
+
}
|
|
89942
|
+
exports.splittingWeight = splittingWeight;
|
|
89943
|
+
function countySplittingWeight(overridesJSON) {
|
|
89944
|
+
const csW = config_json_1.default.traditionalPrinciples.splitting.county.weight;
|
|
89945
|
+
return csW;
|
|
89946
|
+
}
|
|
89947
|
+
exports.countySplittingWeight = countySplittingWeight;
|
|
89948
|
+
function countySplittingRange(overridesJSON) {
|
|
89949
|
+
const range = config_json_1.default.traditionalPrinciples.splitting.county.range;
|
|
89950
|
+
return range;
|
|
89951
|
+
}
|
|
89952
|
+
exports.countySplittingRange = countySplittingRange;
|
|
89953
|
+
function allowableSplitsMultiplier(overridesJSON) {
|
|
89954
|
+
const m = config_json_1.default.traditionalPrinciples.splitting.county.allowableSplitsMultiplier;
|
|
89955
|
+
return m;
|
|
89956
|
+
}
|
|
89957
|
+
exports.allowableSplitsMultiplier = allowableSplitsMultiplier;
|
|
89958
|
+
function districtSplittingWeight(overridesJSON) {
|
|
89959
|
+
const dsW = config_json_1.default.traditionalPrinciples.splitting.district.weight;
|
|
89960
|
+
return dsW;
|
|
89961
|
+
}
|
|
89962
|
+
exports.districtSplittingWeight = districtSplittingWeight;
|
|
89963
|
+
function districtSplittingRange(overridesJSON) {
|
|
89964
|
+
const range = config_json_1.default.traditionalPrinciples.splitting.district.range;
|
|
89965
|
+
return range;
|
|
89966
|
+
}
|
|
89967
|
+
exports.districtSplittingRange = districtSplittingRange;
|
|
89968
|
+
function popdevWeight(context, overridesJSON) {
|
|
89969
|
+
const pdW = config_json_1.default.traditionalPrinciples.popdev.weight[context];
|
|
89970
|
+
return pdW;
|
|
89971
|
+
}
|
|
89972
|
+
exports.popdevWeight = popdevWeight;
|
|
89973
|
+
// NOTE - Raw ranges, not inverted (i.e., smaller is better)
|
|
89974
|
+
// NOTE - This could be optimized to not calc LD values for CD's (or do it once)
|
|
89975
|
+
function popdevRange(bLegislative, overridesJSON) {
|
|
89976
|
+
const cdRange = config_json_1.default.traditionalPrinciples.popdev.range[0 /* Congressional */];
|
|
89977
|
+
const worstCD = cdRange[exports.BEG];
|
|
89978
|
+
const bestCD = cdRange[exports.END];
|
|
89979
|
+
const ldRange = config_json_1.default.traditionalPrinciples.popdev.range[1 /* StateLegislative */];
|
|
89980
|
+
const iRange = bLegislative ? ldRange : cdRange;
|
|
89981
|
+
const worst = iRange[exports.BEG];
|
|
89982
|
+
const best = bLegislative ? (bestCD / worstCD) * iRange[exports.BEG] : iRange[exports.END];
|
|
89983
|
+
// Invert the range, so bigger is better.
|
|
89984
|
+
return [worst, best];
|
|
89985
|
+
}
|
|
89986
|
+
exports.popdevRange = popdevRange;
|
|
89987
|
+
// NOTE - Raw threshold, not inverted (i.e., smaller is better)
|
|
89988
|
+
function popdevThreshold(bLegislative, overridesJSON) {
|
|
89989
|
+
const threshold = popdevRange(bLegislative)[exports.BEG];
|
|
89990
|
+
return threshold;
|
|
89991
|
+
}
|
|
89992
|
+
exports.popdevThreshold = popdevThreshold;
|
|
89993
|
+
// OVERALL SCORE
|
|
89994
|
+
function partisanWeight(context, overridesJSON) {
|
|
89995
|
+
const pW = config_json_1.default.partisan.weight[context];
|
|
89996
|
+
return pW;
|
|
89997
|
+
}
|
|
89998
|
+
exports.partisanWeight = partisanWeight;
|
|
89999
|
+
function traditionalPrinciplesWeight(context, overridesJSON) {
|
|
90000
|
+
const tpW = config_json_1.default.traditionalPrinciples.weight[context];
|
|
90001
|
+
return tpW;
|
|
90002
|
+
}
|
|
90003
|
+
exports.traditionalPrinciplesWeight = traditionalPrinciplesWeight;
|
|
90004
|
+
|
|
90005
|
+
|
|
89773
90006
|
/***/ }),
|
|
89774
90007
|
|
|
89775
90008
|
/***/ "./src/equal.ts":
|
|
@@ -89793,7 +90026,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
89793
90026
|
};
|
|
89794
90027
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
89795
90028
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
89796
|
-
const
|
|
90029
|
+
const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
89797
90030
|
const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
|
|
89798
90031
|
// Migrated from district-analytics
|
|
89799
90032
|
// Expect a full dict of district IDs & values (which might be zero = empty)
|
|
@@ -89814,7 +90047,7 @@ function doPopulationDeviation(e, targetSize, bLegislative, bLog = false) {
|
|
|
89814
90047
|
// Round the raw value to the desired level of precision
|
|
89815
90048
|
const popDev = U.trim((max - min) / targetSize);
|
|
89816
90049
|
const score = scorePopulationDeviation(popDev, bLegislative);
|
|
89817
|
-
const threshold = bLegislative
|
|
90050
|
+
const threshold = C.popdevThreshold(bLegislative);
|
|
89818
90051
|
const notes = {
|
|
89819
90052
|
'maxDeviation': max - min,
|
|
89820
90053
|
'threshold': threshold
|
|
@@ -89830,11 +90063,11 @@ function doPopulationDeviation(e, targetSize, bLegislative, bLog = false) {
|
|
|
89830
90063
|
exports.doPopulationDeviation = doPopulationDeviation;
|
|
89831
90064
|
function scorePopulationDeviation(rawValue, bLegislative) {
|
|
89832
90065
|
const _normalizer = new normalize_1.Normalizer(rawValue);
|
|
89833
|
-
|
|
89834
|
-
const
|
|
90066
|
+
// Raw range in not inverted (i.e., smaller is better)
|
|
90067
|
+
const range = C.popdevRange(bLegislative);
|
|
89835
90068
|
_normalizer.invert();
|
|
89836
|
-
_normalizer.clip(
|
|
89837
|
-
_normalizer.unitize(
|
|
90069
|
+
_normalizer.clip(1.0 - range[C.BEG], 1.0 - range[C.END]);
|
|
90070
|
+
_normalizer.unitize(1.0 - range[C.BEG], 1.0 - range[C.END]);
|
|
89838
90071
|
_normalizer.rescale();
|
|
89839
90072
|
return _normalizer.normalizedNum;
|
|
89840
90073
|
}
|
|
@@ -89843,10 +90076,257 @@ exports.scorePopulationDeviation = scorePopulationDeviation;
|
|
|
89843
90076
|
|
|
89844
90077
|
/***/ }),
|
|
89845
90078
|
|
|
89846
|
-
/***/ "./src/
|
|
89847
|
-
|
|
89848
|
-
!*** ./src/
|
|
89849
|
-
|
|
90079
|
+
/***/ "./src/index.ts":
|
|
90080
|
+
/*!**********************!*\
|
|
90081
|
+
!*** ./src/index.ts ***!
|
|
90082
|
+
\**********************/
|
|
90083
|
+
/*! no static exports found */
|
|
90084
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
90085
|
+
|
|
90086
|
+
"use strict";
|
|
90087
|
+
|
|
90088
|
+
//
|
|
90089
|
+
// SCORE PACKAGE API
|
|
90090
|
+
//
|
|
90091
|
+
function __export(m) {
|
|
90092
|
+
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
|
90093
|
+
}
|
|
90094
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90095
|
+
__export(__webpack_require__(/*! ./_api */ "./src/_api.ts"));
|
|
90096
|
+
var types_1 = __webpack_require__(/*! ./types */ "./src/types.ts");
|
|
90097
|
+
exports.sampleScorecard = types_1.sampleScorecard;
|
|
90098
|
+
exports.sampleProfile = types_1.sampleProfile;
|
|
90099
|
+
|
|
90100
|
+
|
|
90101
|
+
/***/ }),
|
|
90102
|
+
|
|
90103
|
+
/***/ "./src/minority.ts":
|
|
90104
|
+
/*!*************************!*\
|
|
90105
|
+
!*** ./src/minority.ts ***!
|
|
90106
|
+
\*************************/
|
|
90107
|
+
/*! no static exports found */
|
|
90108
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
90109
|
+
|
|
90110
|
+
"use strict";
|
|
90111
|
+
|
|
90112
|
+
//
|
|
90113
|
+
// SCORING FOR MINORITY OPPORTUNITY
|
|
90114
|
+
//
|
|
90115
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
90116
|
+
if (mod && mod.__esModule) return mod;
|
|
90117
|
+
var result = {};
|
|
90118
|
+
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
90119
|
+
result["default"] = mod;
|
|
90120
|
+
return result;
|
|
90121
|
+
};
|
|
90122
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90123
|
+
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
90124
|
+
const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
90125
|
+
const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
|
|
90126
|
+
const partisan_1 = __webpack_require__(/*! ./partisan */ "./src/partisan.ts");
|
|
90127
|
+
function evalMinorityOpportunity(p, bLog = false) {
|
|
90128
|
+
// Calculate average Democratic win share
|
|
90129
|
+
const VfArray = p.partisanProfile.vfArray;
|
|
90130
|
+
const DWins = VfArray.filter(x => x > 0.5);
|
|
90131
|
+
const averageDVf = (DWins.length > 0) ? U.avgArray(DWins) : undefined;
|
|
90132
|
+
// Determine proportional minority districts by demographic (ignore 'White')
|
|
90133
|
+
const districtsByDemo = calcDistrictsByDemo(p.demographicProfile.stateMfArray.slice(1), p.nDistricts);
|
|
90134
|
+
// Initialize arrays for results
|
|
90135
|
+
let offset = 1; // Don't process 'White'
|
|
90136
|
+
let nDemos = 6 /* Native */ + 1 - offset; // Ditto
|
|
90137
|
+
let nBuckets = 2; // 37–50% and > 50%
|
|
90138
|
+
const demosByDistrict = p.demographicProfile.mfArrayByDistrict;
|
|
90139
|
+
let bucketsByDemo = new Array(nDemos);
|
|
90140
|
+
for (let j = 0; j < nDemos; j++) {
|
|
90141
|
+
bucketsByDemo[j] = [0, 0];
|
|
90142
|
+
}
|
|
90143
|
+
let opptyByDemo = U.initArray(nDemos, 0.0);
|
|
90144
|
+
// For each district
|
|
90145
|
+
for (let i = 0; i < p.nDistricts; i++) {
|
|
90146
|
+
// Find the opportunities for minority representation
|
|
90147
|
+
for (let j = 0; j < nDemos; j++) {
|
|
90148
|
+
const proportion = U.deepCopy(demosByDistrict[i][j + offset]);
|
|
90149
|
+
if (exceedsMinimumThreshold(proportion)) {
|
|
90150
|
+
// Bucket opportunity districts
|
|
90151
|
+
const bucket = (exceedsMaximumThreshold(proportion)) ? 1 : 0;
|
|
90152
|
+
bucketsByDemo[j][bucket] += 1;
|
|
90153
|
+
// Accumulate seat probabilities
|
|
90154
|
+
opptyByDemo[j] += estMinorityOpportunity(proportion);
|
|
90155
|
+
}
|
|
90156
|
+
}
|
|
90157
|
+
}
|
|
90158
|
+
// Sum the # of level 1 (37–50%) and level 2 (> 50%) opportunity districts - ignore total minority
|
|
90159
|
+
let l1 = 0;
|
|
90160
|
+
let l2 = 0;
|
|
90161
|
+
bucketsByDemo.slice(1).forEach(function (buckets) {
|
|
90162
|
+
l1 += buckets[0 /* Level1 */];
|
|
90163
|
+
l2 += buckets[1 /* Level2 */];
|
|
90164
|
+
});
|
|
90165
|
+
// Sum the # of opportunity districts - ignore total minority
|
|
90166
|
+
const oD = U.sumArray(opptyByDemo.slice(1));
|
|
90167
|
+
// Sum the # of proportion districts - ignore total minority
|
|
90168
|
+
const pD = U.sumArray(districtsByDemo.slice(1));
|
|
90169
|
+
// Score opportunity
|
|
90170
|
+
const score = scoreMinority(oD, pD);
|
|
90171
|
+
let mS = {
|
|
90172
|
+
report: {
|
|
90173
|
+
averageDVf: averageDVf,
|
|
90174
|
+
bucketsByDemographic: bucketsByDemo
|
|
90175
|
+
},
|
|
90176
|
+
nOpportunity1: l1,
|
|
90177
|
+
nOpportunity2: l2,
|
|
90178
|
+
nProportional: pD,
|
|
90179
|
+
opportunityDistricts: oD,
|
|
90180
|
+
score: score
|
|
90181
|
+
};
|
|
90182
|
+
return mS;
|
|
90183
|
+
}
|
|
90184
|
+
exports.evalMinorityOpportunity = evalMinorityOpportunity;
|
|
90185
|
+
function scoreMinority(oD, pD) {
|
|
90186
|
+
const bonus = C.minorityBonus();
|
|
90187
|
+
const score = (pD > 0) ? Math.round((Math.min(oD, pD) / pD) * bonus) : 0;
|
|
90188
|
+
return score;
|
|
90189
|
+
}
|
|
90190
|
+
exports.scoreMinority = scoreMinority;
|
|
90191
|
+
function calcDistrictsByDemo(MfArray, N) {
|
|
90192
|
+
const districtsByDemo = MfArray.map(v => calcProportionalDistricts(v, N));
|
|
90193
|
+
return districtsByDemo;
|
|
90194
|
+
}
|
|
90195
|
+
exports.calcDistrictsByDemo = calcDistrictsByDemo;
|
|
90196
|
+
function estMinorityOpportunity(Mf) {
|
|
90197
|
+
const _normalizer = new normalize_1.Normalizer(Mf);
|
|
90198
|
+
// NOTE - Shift proportions, so 37% minority scores like 49% partisan
|
|
90199
|
+
const shift = C.minorityShift();
|
|
90200
|
+
_normalizer.wipNum += shift;
|
|
90201
|
+
const range = C.minorityOpportunityDistribution();
|
|
90202
|
+
const distBeg = range[C.BEG];
|
|
90203
|
+
const distEnd = range[C.END];
|
|
90204
|
+
_normalizer.clip(distBeg, distEnd);
|
|
90205
|
+
_normalizer.unitize(distBeg, distEnd);
|
|
90206
|
+
const oppty = partisan_1.estSeatProbability(_normalizer.wipNum, range);
|
|
90207
|
+
return oppty;
|
|
90208
|
+
}
|
|
90209
|
+
exports.estMinorityOpportunity = estMinorityOpportunity;
|
|
90210
|
+
// HELPERS
|
|
90211
|
+
function exceedsMinimumThreshold(Mf) {
|
|
90212
|
+
const threshold = C.minorityOpportunityRange()[C.BEG];
|
|
90213
|
+
return Mf >= threshold;
|
|
90214
|
+
}
|
|
90215
|
+
function exceedsMaximumThreshold(Mf) {
|
|
90216
|
+
const threshold = C.minorityOpportunityRange()[C.END];
|
|
90217
|
+
return Mf > threshold;
|
|
90218
|
+
}
|
|
90219
|
+
function calcProportionalDistricts(proportion, nDistricts) {
|
|
90220
|
+
// TODO - Bump up to get a statewide proportion on the bubble to rate one district?
|
|
90221
|
+
const roundUp = 0.0;
|
|
90222
|
+
const fractional = proportion * nDistricts;
|
|
90223
|
+
const integral = Math.round(fractional + roundUp);
|
|
90224
|
+
return integral;
|
|
90225
|
+
}
|
|
90226
|
+
exports.calcProportionalDistricts = calcProportionalDistricts;
|
|
90227
|
+
|
|
90228
|
+
|
|
90229
|
+
/***/ }),
|
|
90230
|
+
|
|
90231
|
+
/***/ "./src/normalize.ts":
|
|
90232
|
+
/*!**************************!*\
|
|
90233
|
+
!*** ./src/normalize.ts ***!
|
|
90234
|
+
\**************************/
|
|
90235
|
+
/*! no static exports found */
|
|
90236
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
90237
|
+
|
|
90238
|
+
"use strict";
|
|
90239
|
+
|
|
90240
|
+
//
|
|
90241
|
+
// NORMALIZATION UTILITIES
|
|
90242
|
+
//
|
|
90243
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
90244
|
+
if (mod && mod.__esModule) return mod;
|
|
90245
|
+
var result = {};
|
|
90246
|
+
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
90247
|
+
result["default"] = mod;
|
|
90248
|
+
return result;
|
|
90249
|
+
};
|
|
90250
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90251
|
+
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
90252
|
+
class Normalizer {
|
|
90253
|
+
constructor(rawValue) {
|
|
90254
|
+
this.rawNum = rawValue;
|
|
90255
|
+
this.wipNum = this.rawNum;
|
|
90256
|
+
}
|
|
90257
|
+
// *Don't* transform the input.
|
|
90258
|
+
identity() {
|
|
90259
|
+
return this.wipNum;
|
|
90260
|
+
}
|
|
90261
|
+
positive() {
|
|
90262
|
+
this.wipNum = Math.abs(this.wipNum);
|
|
90263
|
+
return this.wipNum;
|
|
90264
|
+
}
|
|
90265
|
+
// Invert a value in the unit range [0.0–1.0] (so that bigger is better).
|
|
90266
|
+
invert() {
|
|
90267
|
+
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
90268
|
+
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Inverting: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
90269
|
+
this.wipNum = 1.0 - this.wipNum;
|
|
90270
|
+
return this.wipNum;
|
|
90271
|
+
}
|
|
90272
|
+
// Constrain the value to be within a specified range.
|
|
90273
|
+
clip(begin_range, end_range) {
|
|
90274
|
+
// Handle the ends of the range being given either order
|
|
90275
|
+
const min_range = Math.min(begin_range, end_range);
|
|
90276
|
+
const max_range = Math.max(begin_range, end_range);
|
|
90277
|
+
this.wipNum = Math.max(Math.min(this.wipNum, max_range), min_range);
|
|
90278
|
+
return this.wipNum;
|
|
90279
|
+
}
|
|
90280
|
+
// Recast the value as the delta from a baseline value. NOOP if it is zero.
|
|
90281
|
+
// NOTE - Values can be + or -.
|
|
90282
|
+
rebase(base) {
|
|
90283
|
+
this.wipNum = this.wipNum - base;
|
|
90284
|
+
return this.wipNum;
|
|
90285
|
+
}
|
|
90286
|
+
// Re-scale a value into the [0.0 – 1.0] range, using a given range.
|
|
90287
|
+
// NOTE - This assumes that values have alrady been clipped into the range.
|
|
90288
|
+
unitize(begin_range, end_range) {
|
|
90289
|
+
const min_range = Math.min(begin_range, end_range);
|
|
90290
|
+
const max_range = Math.max(begin_range, end_range);
|
|
90291
|
+
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
90292
|
+
console.assert(((this.wipNum >= min_range) && (this.wipNum <= max_range)), "Unitizing: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
90293
|
+
const ranged = this.wipNum - min_range;
|
|
90294
|
+
this.wipNum = Math.abs(ranged / (end_range - begin_range));
|
|
90295
|
+
return this.wipNum;
|
|
90296
|
+
}
|
|
90297
|
+
// Decay a value in the unit range [0.0–1.0] by its distance from zero.
|
|
90298
|
+
// NOTE - If the range is already such that "bigger is better," then the closer
|
|
90299
|
+
// the value is to 1.0 (the best) the *less* it will decay.
|
|
90300
|
+
decay(fn = 0 /* Gravity */) {
|
|
90301
|
+
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
90302
|
+
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Decaying: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
90303
|
+
switch (fn) {
|
|
90304
|
+
case 0 /* Gravity */: {
|
|
90305
|
+
this.wipNum = Math.pow(this.wipNum, S.DISTANCE_WEIGHT);
|
|
90306
|
+
return this.wipNum;
|
|
90307
|
+
}
|
|
90308
|
+
default: {
|
|
90309
|
+
throw new Error("Decay function not recognized.");
|
|
90310
|
+
}
|
|
90311
|
+
}
|
|
90312
|
+
}
|
|
90313
|
+
// Translate a value in the unit range to the user-friendly range [0 – 100].
|
|
90314
|
+
rescale() {
|
|
90315
|
+
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
90316
|
+
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Rescaling: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
90317
|
+
this.normalizedNum = Math.round(this.wipNum * S.NORMALIZED_RANGE);
|
|
90318
|
+
return this.normalizedNum;
|
|
90319
|
+
}
|
|
90320
|
+
}
|
|
90321
|
+
exports.Normalizer = Normalizer;
|
|
90322
|
+
|
|
90323
|
+
|
|
90324
|
+
/***/ }),
|
|
90325
|
+
|
|
90326
|
+
/***/ "./src/partisan.ts":
|
|
90327
|
+
/*!*************************!*\
|
|
90328
|
+
!*** ./src/partisan.ts ***!
|
|
90329
|
+
\*************************/
|
|
89850
90330
|
/*! no static exports found */
|
|
89851
90331
|
/***/ (function(module, exports, __webpack_require__) {
|
|
89852
90332
|
|
|
@@ -89865,117 +90345,159 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
89865
90345
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
89866
90346
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
89867
90347
|
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
90348
|
+
const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
89868
90349
|
const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
|
|
89869
90350
|
// NOTE - I'm passing T.VfArray's into everything. District indices = array indices.
|
|
89870
90351
|
// NOTE - I do not (cannot) assume that the values are sorted.
|
|
89871
90352
|
// SCORE BIAS & COMPETITIVENESS
|
|
89872
|
-
/* SCORECARD FIELDS
|
|
89873
|
-
|
|
89874
|
-
* ^S# [
|
|
89875
|
-
* ^S% [
|
|
89876
|
-
*
|
|
89877
|
-
* S# [
|
|
89878
|
-
* S% [
|
|
89879
|
-
* B% [
|
|
89880
|
-
* B$ [
|
|
89881
|
-
|
|
89882
|
-
*
|
|
89883
|
-
*
|
|
89884
|
-
|
|
89885
|
-
*
|
|
89886
|
-
*
|
|
89887
|
-
*
|
|
89888
|
-
|
|
89889
|
-
|
|
89890
|
-
*
|
|
90353
|
+
/* SCORECARD FIELDS
|
|
90354
|
+
|
|
90355
|
+
* ^S# [bestS] = the Democratic seats closest to proportional
|
|
90356
|
+
* ^S% [bestSf] = the corresponding Democratic seat share
|
|
90357
|
+
* S! [fptpS] = the estimated number of Democratic seats using first past the post
|
|
90358
|
+
* S# [probableS] = the estimated Democratic seats, using seat probabilities
|
|
90359
|
+
* S% [probableSf] = the estimated Democratic seat share fraction, calculated as S# / N
|
|
90360
|
+
* B% [bias]= the bias calculated as S% – ^S%
|
|
90361
|
+
* B$ [bias.score] = the bias score normalized [0–100]
|
|
90362
|
+
|
|
90363
|
+
* UE# [unearnedS] = the number of unearned seats (R = positive; D = negative)
|
|
90364
|
+
* I$ [impact.score] -- the unearned seats normalized [0–100] as impact
|
|
90365
|
+
|
|
90366
|
+
* R# [r] = the responsiveness at V%
|
|
90367
|
+
* rD [rD] = the estimated # of responsive districts (using probabilities)
|
|
90368
|
+
* rD% [rDf] = the estimated # of responsive districts, as a fraction of N
|
|
90369
|
+
|
|
90370
|
+
* C [c] = the number of districts that fall into the range [45–55%]
|
|
90371
|
+
* cD [cD] = the estimated # of competitive districts, using probabilities & a narrower [0.25–0.75] range
|
|
90372
|
+
* cD% [cDf] = the estimated # of competitive districts, as a fraction of N
|
|
90373
|
+
* beg/end [mRange] = the 1–N indices for the first and last district that separate the likely result and the proportional result
|
|
90374
|
+
* Md [mD] = the competitiveness of the marginal districts
|
|
90375
|
+
* Md% [mDf] = the probability that the marginal seats will flip, so S% => ^S%
|
|
90376
|
+
* C$ [competitiveness.score] = the competitiveness score normalized [0–100]
|
|
90377
|
+
|
|
90378
|
+
* <P$ [score] = the combined partisan score used to compare plans *across* states
|
|
90379
|
+
* >P$ [score2] = the combined partisan score used to compare plans *within* a state, along with traditional districting principles
|
|
89891
90380
|
|
|
89892
90381
|
*/
|
|
89893
|
-
function scorePartisan(Vf, VfArray,
|
|
90382
|
+
function scorePartisan(Vf, VfArray, bAll = false, bConstrained = true) {
|
|
89894
90383
|
const N = VfArray.length;
|
|
89895
|
-
const
|
|
89896
|
-
const
|
|
89897
|
-
const
|
|
89898
|
-
const
|
|
89899
|
-
const
|
|
89900
|
-
const
|
|
89901
|
-
const
|
|
89902
|
-
const
|
|
89903
|
-
const
|
|
89904
|
-
const
|
|
89905
|
-
const
|
|
89906
|
-
const
|
|
89907
|
-
const
|
|
89908
|
-
const
|
|
89909
|
-
const
|
|
90384
|
+
const bestS = bestSeats(N, Vf);
|
|
90385
|
+
const bestSf = bestSeatShare(bestS, N);
|
|
90386
|
+
const fptpS = bAll ? estFPTPSeats(VfArray) : undefined;
|
|
90387
|
+
const range = bConstrained ? C.competitiveDistribution() : undefined;
|
|
90388
|
+
const probableS = estprobableSeats(VfArray, range);
|
|
90389
|
+
const probableSf = estprobableSeatShare(probableS, N);
|
|
90390
|
+
const bias = estbias(probableSf, bestSf);
|
|
90391
|
+
const biasScore = scorebias(bias, Vf, probableSf, N);
|
|
90392
|
+
const unearnedS = estunearnedSeats(bestS, probableS);
|
|
90393
|
+
const impactScore = scoreImpact(unearnedS);
|
|
90394
|
+
const bInferSV = (bAll || (!bConstrained));
|
|
90395
|
+
const inferredSVpoints = bInferSV ? inferSVpoints(Vf, VfArray, range) : undefined;
|
|
90396
|
+
const R = bInferSV ? estResponsiveness(Vf, inferredSVpoints) : undefined;
|
|
90397
|
+
const rD = bInferSV ? estResponsiveDistricts(VfArray) : undefined;
|
|
90398
|
+
const rDf = bInferSV ? estResponsiveDistrictsShare(rD, N) : undefined;
|
|
90399
|
+
const Cn = countCompetitiveDistricts(VfArray);
|
|
90400
|
+
const cD = bConstrained ? estCompetitiveDistricts(VfArray) : rD;
|
|
90401
|
+
const cDf = estCompetitiveDistrictsShare(cD, N);
|
|
90402
|
+
const Mrange = findMarginalDistricts(Vf, VfArray, N);
|
|
90403
|
+
const Md = estMarginalCompetitiveDistricts(Mrange, VfArray);
|
|
90404
|
+
const Mdf = estMarginalCompetitiveShare(Md, Mrange);
|
|
90405
|
+
const competitivenessScore = scoreCompetitiveness(Mdf, cDf);
|
|
89910
90406
|
const biasScoring = {
|
|
89911
|
-
|
|
89912
|
-
|
|
89913
|
-
|
|
89914
|
-
|
|
89915
|
-
|
|
89916
|
-
|
|
89917
|
-
|
|
90407
|
+
bestS: bestS,
|
|
90408
|
+
bestSf: bestSf,
|
|
90409
|
+
fptpS: fptpS,
|
|
90410
|
+
probableS: probableS,
|
|
90411
|
+
probableSf: probableSf,
|
|
90412
|
+
bias: bias,
|
|
90413
|
+
score: biasScore
|
|
89918
90414
|
};
|
|
89919
|
-
|
|
89920
|
-
|
|
89921
|
-
|
|
89922
|
-
|
|
89923
|
-
|
|
89924
|
-
|
|
89925
|
-
|
|
89926
|
-
|
|
89927
|
-
|
|
89928
|
-
|
|
89929
|
-
|
|
89930
|
-
|
|
89931
|
-
|
|
89932
|
-
|
|
89933
|
-
|
|
89934
|
-
|
|
89935
|
-
|
|
89936
|
-
|
|
89937
|
-
|
|
89938
|
-
|
|
90415
|
+
const impactScoring = {
|
|
90416
|
+
unearnedS: unearnedS,
|
|
90417
|
+
score: impactScore
|
|
90418
|
+
};
|
|
90419
|
+
let competitiveScoring = {
|
|
90420
|
+
r: R,
|
|
90421
|
+
rD: rD,
|
|
90422
|
+
rDf: rDf,
|
|
90423
|
+
c: Cn,
|
|
90424
|
+
cD: cD,
|
|
90425
|
+
cDf: cDf,
|
|
90426
|
+
mRange: Mrange,
|
|
90427
|
+
mD: Md,
|
|
90428
|
+
mDf: Mdf,
|
|
90429
|
+
score: competitivenessScore
|
|
90430
|
+
};
|
|
90431
|
+
if (bAll) {
|
|
90432
|
+
competitiveScoring.r = R;
|
|
90433
|
+
competitiveScoring.rD = rD;
|
|
90434
|
+
competitiveScoring.rDf = rDf;
|
|
90435
|
+
}
|
|
90436
|
+
// Weight bias, impact, and competitiveness into partisan scores to compare
|
|
90437
|
+
// plans across states and within a state.
|
|
90438
|
+
const bS = biasScoring.score;
|
|
90439
|
+
const iS = impactScoring.score;
|
|
90440
|
+
const cS = competitiveScoring.score;
|
|
90441
|
+
const acrossStatesPartisanScore = weightPartisan(bS, iS, cS, 0 /* AcrossStates */);
|
|
90442
|
+
const withinStatesPartisanScore = weightPartisan(bS, iS, cS, 1 /* WithinAState */);
|
|
89939
90443
|
const s = {
|
|
89940
90444
|
bias: biasScoring,
|
|
89941
|
-
|
|
90445
|
+
impact: impactScoring,
|
|
90446
|
+
competitiveness: competitiveScoring,
|
|
90447
|
+
score: acrossStatesPartisanScore,
|
|
90448
|
+
score2: withinStatesPartisanScore
|
|
89942
90449
|
};
|
|
89943
90450
|
return s;
|
|
89944
90451
|
}
|
|
89945
90452
|
exports.scorePartisan = scorePartisan;
|
|
89946
|
-
function weightPartisan(
|
|
89947
|
-
|
|
90453
|
+
function weightPartisan(bS, iS, cS, context) {
|
|
90454
|
+
const bW = C.biasWeight(context);
|
|
90455
|
+
const iW = C.impactWeight(context);
|
|
90456
|
+
const cW = C.competitivenessWeight(context);
|
|
90457
|
+
const score = Math.round(((bS * bW) + (iS * iW) + (cS * cW)) / (bW + iW + cW));
|
|
90458
|
+
return score;
|
|
89948
90459
|
}
|
|
89949
90460
|
exports.weightPartisan = weightPartisan;
|
|
89950
|
-
function
|
|
89951
|
-
console.log('XX, Name, N, V%, ^S#, ^S%,
|
|
90461
|
+
function printPartisanScorecardHeader() {
|
|
90462
|
+
console.log('XX, Name, N, 1/N%, V%, ^S#, ^S%, S!, S#, S%, B%, B$, UE#, I$, R#, Rd, Rd%, C#, Cd, Cd%, beg, end, Md, Md%, C$, <P$, >P$');
|
|
89952
90463
|
}
|
|
89953
|
-
exports.
|
|
89954
|
-
function
|
|
89955
|
-
console.log('%s, %s, %i, %f, %i, %f, %i, %f, %f, %f, %i, %f, %f, %f, %f, %f, %f, %f, %i', xx, name, N, Vf, s.bias.
|
|
90464
|
+
exports.printPartisanScorecardHeader = printPartisanScorecardHeader;
|
|
90465
|
+
function printPartisanScorecardRow(xx, name, N, Vf, s) {
|
|
90466
|
+
console.log('%s, %s, %i, %f, %f, %i, %f, %i, %f, %f, %f, %i, %f, %i, %f, %f, %f, %i, %f, %f, %i, %i, %f, %f, %i, %i, %i', xx, name, N, U.trim(1 / N), Vf, s.bias.bestS, s.bias.bestSf, s.bias.fptpS, s.bias.probableS, s.bias.probableSf, s.bias.bias, s.bias.score, s.impact.unearnedS, s.impact.score, s.competitiveness.r, s.competitiveness.rD, s.competitiveness.rDf, s.competitiveness.c, s.competitiveness.cD, s.competitiveness.cDf, s.competitiveness.mRange[C.BEG], s.competitiveness.mRange[C.END], s.competitiveness.mD, s.competitiveness.mDf, s.competitiveness.score, s.score, s.score2);
|
|
89956
90467
|
}
|
|
89957
|
-
exports.
|
|
89958
|
-
function
|
|
90468
|
+
exports.printPartisanScorecardRow = printPartisanScorecardRow;
|
|
90469
|
+
function scorebias(rawbias, Vf, Sf, N) {
|
|
89959
90470
|
if (isAntimajoritarian(Vf, Sf)) {
|
|
89960
90471
|
return 0;
|
|
89961
90472
|
}
|
|
89962
90473
|
else {
|
|
89963
|
-
const _normalizer = new normalize_1.Normalizer(
|
|
89964
|
-
const
|
|
89965
|
-
const
|
|
89966
|
-
const best = S.BIAS_BEST;
|
|
90474
|
+
const _normalizer = new normalize_1.Normalizer(rawbias);
|
|
90475
|
+
const worst = C.biasRange()[C.BEG];
|
|
90476
|
+
const best = C.biasRange()[C.END];
|
|
89967
90477
|
_normalizer.clip(worst, best);
|
|
89968
90478
|
_normalizer.unitize(worst, best);
|
|
89969
90479
|
_normalizer.invert();
|
|
89970
|
-
// TODO - SCORE: Would also decaying raw values (e.g., by squaring them) be too
|
|
89971
|
-
// much with a small range (like 10% or one seat)?
|
|
89972
90480
|
// _normalizer.decay();
|
|
89973
90481
|
_normalizer.rescale();
|
|
89974
90482
|
const score = _normalizer.normalizedNum;
|
|
89975
90483
|
return score;
|
|
89976
90484
|
}
|
|
89977
90485
|
}
|
|
89978
|
-
exports.
|
|
90486
|
+
exports.scorebias = scorebias;
|
|
90487
|
+
// Normalize unearned seats
|
|
90488
|
+
function scoreImpact(rawUE) {
|
|
90489
|
+
const _normalizer = new normalize_1.Normalizer(rawUE);
|
|
90490
|
+
const worst = C.unearnedThreshold();
|
|
90491
|
+
const best = 0.0;
|
|
90492
|
+
_normalizer.positive();
|
|
90493
|
+
_normalizer.clip(worst, best);
|
|
90494
|
+
_normalizer.unitize(worst, best);
|
|
90495
|
+
_normalizer.invert();
|
|
90496
|
+
_normalizer.rescale();
|
|
90497
|
+
const score = _normalizer.normalizedNum;
|
|
90498
|
+
return score;
|
|
90499
|
+
}
|
|
90500
|
+
exports.scoreImpact = scoreImpact;
|
|
89979
90501
|
function isAntimajoritarian(Vf, Sf) {
|
|
89980
90502
|
const bDem = ((Vf < 0.5) && (Sf > 0.5)) ? true : false;
|
|
89981
90503
|
const bRep = (((1 - Vf) < 0.5) && ((1 - Sf) > 0.5)) ? true : false;
|
|
@@ -89987,16 +90509,24 @@ function scoreCompetitiveness(rawMarginal, rawOverall) {
|
|
|
89987
90509
|
// But the practical max is more like 2/3's, so unitize that range to [0.0–1.0].
|
|
89988
90510
|
// Then scale the values to [0–100].
|
|
89989
90511
|
const _overall = new normalize_1.Normalizer(rawOverall);
|
|
89990
|
-
|
|
89991
|
-
|
|
90512
|
+
let worst = C.overallCompetitivenessRange()[C.BEG];
|
|
90513
|
+
let best = C.overallCompetitivenessRange()[C.END];
|
|
90514
|
+
_overall.clip(worst, best);
|
|
90515
|
+
_overall.unitize(worst, best);
|
|
89992
90516
|
_overall.rescale();
|
|
89993
|
-
const
|
|
90517
|
+
const ocS = _overall.normalizedNum;
|
|
89994
90518
|
// Normalize marginal competitiveness
|
|
89995
90519
|
const _marginal = new normalize_1.Normalizer(rawMarginal);
|
|
90520
|
+
worst = C.marginalCompetitivenessRange()[C.BEG];
|
|
90521
|
+
best = C.marginalCompetitivenessRange()[C.END];
|
|
90522
|
+
_marginal.clip(worst, best);
|
|
90523
|
+
_marginal.unitize(worst, best);
|
|
89996
90524
|
_marginal.rescale();
|
|
89997
|
-
const
|
|
90525
|
+
const mcS = _marginal.normalizedNum;
|
|
90526
|
+
const mcW = C.marginalCompetitivenessWeight();
|
|
90527
|
+
const ocW = C.overallCompetitivenessWeight();
|
|
89998
90528
|
// Then combine the results
|
|
89999
|
-
const score = Math.round(((
|
|
90529
|
+
const score = Math.round(((mcW * mcS) + (ocW * ocS)) / (mcW + ocW));
|
|
90000
90530
|
return score;
|
|
90001
90531
|
}
|
|
90002
90532
|
exports.scoreCompetitiveness = scoreCompetitiveness;
|
|
@@ -90006,23 +90536,40 @@ const { erf } = __webpack_require__(/*! mathjs */ "./node_modules/mathjs/main/es
|
|
|
90006
90536
|
// console.log("erf(-0.5) =", erf(-0.5)); // returns -0.5204998778130465
|
|
90007
90537
|
// console.log("erf(4) =", erf(4)); // returns 0.9999999845827421
|
|
90008
90538
|
// Estimate the probability of a seat win for district, given a Vf
|
|
90009
|
-
function estSeatProbability(Vf) {
|
|
90539
|
+
function estSeatProbability(Vf, range) {
|
|
90540
|
+
if (range) {
|
|
90541
|
+
const _normalizer = new normalize_1.Normalizer(Vf);
|
|
90542
|
+
// The end points of a compressed probability distribution
|
|
90543
|
+
// NOTE - These aren't the points where races start or stop being contested,
|
|
90544
|
+
// just the end points of a distribution that yields the desired behavior
|
|
90545
|
+
// in the typical competitive range [45-55%].
|
|
90546
|
+
const distBeg = range[C.BEG];
|
|
90547
|
+
const distEnd = range[C.END];
|
|
90548
|
+
_normalizer.clip(distBeg, distEnd);
|
|
90549
|
+
_normalizer.unitize(distBeg, distEnd);
|
|
90550
|
+
return seatProbabilityFn(_normalizer.wipNum);
|
|
90551
|
+
}
|
|
90552
|
+
else {
|
|
90553
|
+
return seatProbabilityFn(Vf);
|
|
90554
|
+
}
|
|
90555
|
+
}
|
|
90556
|
+
exports.estSeatProbability = estSeatProbability;
|
|
90557
|
+
function seatProbabilityFn(Vf) {
|
|
90010
90558
|
// Python: 0.5 * (1 + erf((vpi - 0.50) / (0.02 * sqrt(8))))
|
|
90011
90559
|
return U.trim(0.5 * (1.0 + erf((Vf - 0.50) / (0.02 * Math.sqrt(8)))));
|
|
90012
90560
|
}
|
|
90013
|
-
exports.estSeatProbability = estSeatProbability;
|
|
90014
90561
|
// Estimate the number of responsive districts [R(d)], given a set of Vf's
|
|
90015
90562
|
function estDistrictResponsiveness(Vf) {
|
|
90016
90563
|
// Python: 1 - 4 * (est_seat_probability(vpi) - 0.5)**2
|
|
90017
90564
|
return U.trim(1.0 - 4.0 * Math.pow((estSeatProbability(Vf) - 0.5), 2));
|
|
90018
90565
|
}
|
|
90019
90566
|
exports.estDistrictResponsiveness = estDistrictResponsiveness;
|
|
90020
|
-
function inferSVpoints(Vf, VfArray) {
|
|
90567
|
+
function inferSVpoints(Vf, VfArray, range) {
|
|
90021
90568
|
const nDistricts = VfArray.length;
|
|
90022
90569
|
let SVpoints = [];
|
|
90023
90570
|
for (let shiftedVf of shiftRange()) {
|
|
90024
90571
|
const shiftedVPI = shiftDistricts(Vf, VfArray, shiftedVf);
|
|
90025
|
-
const shiftedSf =
|
|
90572
|
+
const shiftedSf = estprobableSeats(shiftedVPI, range) / nDistricts;
|
|
90026
90573
|
SVpoints.push({ v: shiftedVf, s: shiftedSf });
|
|
90027
90574
|
}
|
|
90028
90575
|
return SVpoints;
|
|
@@ -90060,8 +90607,9 @@ function shiftRange() {
|
|
|
90060
90607
|
}
|
|
90061
90608
|
// ESTIMATE BIAS ("FAIR")
|
|
90062
90609
|
// ^S# - The # of Democratic seats closest to proportional @ statewide Vf
|
|
90610
|
+
// The "expected number of seats" from http://bit.ly/2Fcuf4q
|
|
90063
90611
|
function bestSeats(N, Vf) {
|
|
90064
|
-
return Math.round(N * Vf);
|
|
90612
|
+
return Math.round((N * Vf) - S.EPSILON);
|
|
90065
90613
|
}
|
|
90066
90614
|
exports.bestSeats = bestSeats;
|
|
90067
90615
|
// ^S% - The corresponding Democratic seat share
|
|
@@ -90070,16 +90618,16 @@ function bestSeatShare(bestS, N) {
|
|
|
90070
90618
|
}
|
|
90071
90619
|
exports.bestSeatShare = bestSeatShare;
|
|
90072
90620
|
// S# - The estimated # of Democratic seats @ statewide Vf, using seat probabilities
|
|
90073
|
-
function
|
|
90621
|
+
function estprobableSeats(VfArray, range) {
|
|
90074
90622
|
// Python: sum([est_seat_probability(vpi) for vpi in vpi_by_district])
|
|
90075
|
-
return U.trim(U.sumArray(VfArray.map(v => estSeatProbability(v))));
|
|
90623
|
+
return U.trim(U.sumArray(VfArray.map(v => estSeatProbability(v, range))));
|
|
90076
90624
|
}
|
|
90077
|
-
exports.
|
|
90625
|
+
exports.estprobableSeats = estprobableSeats;
|
|
90078
90626
|
// S% - The estimated Democratic seat share fraction @ statewide Vf
|
|
90079
|
-
function
|
|
90627
|
+
function estprobableSeatShare(probableS, N) {
|
|
90080
90628
|
return U.trim(probableS / N);
|
|
90081
90629
|
}
|
|
90082
|
-
exports.
|
|
90630
|
+
exports.estprobableSeatShare = estprobableSeatShare;
|
|
90083
90631
|
// F# - The estimated number of Democratic seats using first past the post
|
|
90084
90632
|
function estFPTPSeats(VfArray) {
|
|
90085
90633
|
// Python: sum([1.0 for vpi in vpi_by_district if (vpi > 0.5)])
|
|
@@ -90094,10 +90642,17 @@ function estFPTPSeats(VfArray) {
|
|
|
90094
90642
|
}
|
|
90095
90643
|
exports.estFPTPSeats = estFPTPSeats;
|
|
90096
90644
|
// B% - Tthe bias calculated as S% – ^S%
|
|
90097
|
-
function
|
|
90645
|
+
function estbias(estSeatShare, bestSeatShare) {
|
|
90098
90646
|
return U.trim(Math.abs(estSeatShare - bestSeatShare));
|
|
90099
90647
|
}
|
|
90100
|
-
exports.
|
|
90648
|
+
exports.estbias = estbias;
|
|
90649
|
+
// UE# - The estimated # of unearned seats
|
|
90650
|
+
// UE_# from http://bit.ly/2Fcuf4q
|
|
90651
|
+
function estunearnedSeats(best, probable) {
|
|
90652
|
+
// NOTE - + values = unearned R seats; – values = unearned D seats
|
|
90653
|
+
return U.trim(best - probable);
|
|
90654
|
+
}
|
|
90655
|
+
exports.estunearnedSeats = estunearnedSeats;
|
|
90101
90656
|
// ESTIMATE RESPONSIVENESS ("COMPETITIVE")
|
|
90102
90657
|
// R# - Estimate responsiveness at the statewide vote share
|
|
90103
90658
|
function estResponsiveness(Vf, inferredSVpoints) {
|
|
@@ -90140,19 +90695,27 @@ function findUpperBracket(Vf, inferredSVpoints) {
|
|
|
90140
90695
|
}
|
|
90141
90696
|
return upperPt;
|
|
90142
90697
|
}
|
|
90143
|
-
//
|
|
90698
|
+
// rD - Estimate the number of responsive districts, given a set of Vf's
|
|
90144
90699
|
function estResponsiveDistricts(VfArray) {
|
|
90145
90700
|
// Python: sum([est_district_responsiveness(vpi) for vpi in vpi_by_district])
|
|
90146
90701
|
return U.trim(U.sumArray(VfArray.map(v => estDistrictResponsiveness(v))));
|
|
90147
90702
|
}
|
|
90148
90703
|
exports.estResponsiveDistricts = estResponsiveDistricts;
|
|
90149
|
-
//
|
|
90150
|
-
function estResponsiveDistrictsShare(
|
|
90151
|
-
return U.trim(
|
|
90704
|
+
// rD% - The estimated # of responsive districts, as a fraction of N
|
|
90705
|
+
function estResponsiveDistrictsShare(rD, N) {
|
|
90706
|
+
return U.trim(rD / N);
|
|
90152
90707
|
}
|
|
90153
90708
|
exports.estResponsiveDistrictsShare = estResponsiveDistrictsShare;
|
|
90154
90709
|
// ESTIMATE COMPETITIVENESS (ENHANCED)
|
|
90155
|
-
//
|
|
90710
|
+
// C - Count the # of competitive districts, defined as v in [45–55%]
|
|
90711
|
+
function countCompetitiveDistricts(VfArray) {
|
|
90712
|
+
return U.trim(U.sumArray(VfArray.map(v => isCompetitive(v))));
|
|
90713
|
+
}
|
|
90714
|
+
exports.countCompetitiveDistricts = countCompetitiveDistricts;
|
|
90715
|
+
function isCompetitive(v) {
|
|
90716
|
+
return ((v >= C.competitiveRange()[C.BEG]) && (v <= C.competitiveRange()[C.END])) ? 1 : 0;
|
|
90717
|
+
}
|
|
90718
|
+
// cD - The estimated # of competitive districts
|
|
90156
90719
|
function estCompetitiveDistricts(VfArray) {
|
|
90157
90720
|
return U.trim(U.sumArray(VfArray.map(v => estDistrictCompetitiveness(v))));
|
|
90158
90721
|
}
|
|
@@ -90160,20 +90723,47 @@ exports.estCompetitiveDistricts = estCompetitiveDistricts;
|
|
|
90160
90723
|
// Re-scale a Democratic vote share to the competitive range (e.g., 45–55%).
|
|
90161
90724
|
function estDistrictCompetitiveness(Vf) {
|
|
90162
90725
|
const _normalizer = new normalize_1.Normalizer(Vf);
|
|
90163
|
-
|
|
90164
|
-
|
|
90726
|
+
// The end points of a compressed probability distribution
|
|
90727
|
+
// NOTE - These aren't the points where races start or stop being contested,
|
|
90728
|
+
// just the end points of a distribution that yields the desired behavior
|
|
90729
|
+
// in the typical competitive range [45-55%].
|
|
90730
|
+
const distBeg = C.competitiveDistribution()[C.BEG];
|
|
90731
|
+
const distEnd = C.competitiveDistribution()[C.END];
|
|
90732
|
+
_normalizer.clip(distBeg, distEnd);
|
|
90733
|
+
_normalizer.unitize(distBeg, distEnd);
|
|
90165
90734
|
const dC = estDistrictResponsiveness(_normalizer.wipNum);
|
|
90166
90735
|
return dC;
|
|
90167
90736
|
}
|
|
90168
90737
|
exports.estDistrictCompetitiveness = estDistrictCompetitiveness;
|
|
90169
|
-
//
|
|
90170
|
-
function estCompetitiveDistrictsShare(
|
|
90171
|
-
return U.trim(
|
|
90738
|
+
// cD% - The estimated # of competitive districts, as a fraction of N
|
|
90739
|
+
function estCompetitiveDistrictsShare(cD, N) {
|
|
90740
|
+
return U.trim(cD / N);
|
|
90172
90741
|
}
|
|
90173
90742
|
exports.estCompetitiveDistrictsShare = estCompetitiveDistrictsShare;
|
|
90743
|
+
// Md - The estimated # of "marginal" districts in and around the likely FPTP
|
|
90744
|
+
// seats & the best seat split that are competitive.
|
|
90745
|
+
function estMarginalCompetitiveDistricts(Mrange, VfArray) {
|
|
90746
|
+
const minId = Mrange[C.BEG];
|
|
90747
|
+
const maxId = Mrange[C.END];
|
|
90748
|
+
// Sort the array values, and subset it to those districts
|
|
90749
|
+
// NOTE - I'm *not* keeping track of the district indexes right now
|
|
90750
|
+
let subsetVfArray = VfArray.sort().slice(minId - 1, maxId);
|
|
90751
|
+
// Est. competitive districts on that array
|
|
90752
|
+
const Md = U.sumArray(subsetVfArray.map(v => estDistrictCompetitiveness(v)));
|
|
90753
|
+
return U.trim(Md);
|
|
90754
|
+
}
|
|
90755
|
+
exports.estMarginalCompetitiveDistricts = estMarginalCompetitiveDistricts;
|
|
90174
90756
|
// Md% - The estimated competitiveness of the "marginal" districts in and around
|
|
90175
90757
|
// the likely FPTP seats & the best seat split as a fraction
|
|
90176
|
-
function estMarginalCompetitiveShare(
|
|
90758
|
+
function estMarginalCompetitiveShare(Md, Mrange) {
|
|
90759
|
+
const minId = Mrange[C.BEG];
|
|
90760
|
+
const maxId = Mrange[C.END];
|
|
90761
|
+
// Est. competitive district share on that result
|
|
90762
|
+
const MdShare = U.trim(estCompetitiveDistrictsShare(Md, maxId - minId + 1));
|
|
90763
|
+
return U.trim(MdShare);
|
|
90764
|
+
}
|
|
90765
|
+
exports.estMarginalCompetitiveShare = estMarginalCompetitiveShare;
|
|
90766
|
+
function findMarginalDistricts(Vf, VfArray, N) {
|
|
90177
90767
|
const bestS = bestSeats(N, Vf);
|
|
90178
90768
|
const fptpS = estFPTPSeats(VfArray);
|
|
90179
90769
|
// Find the marginal districts IDs (indexed 1–N)
|
|
@@ -90192,137 +90782,9 @@ function estMarginalCompetitiveShare(Vf, VfArray, N) {
|
|
|
90192
90782
|
minId = Math.max((N - bestS) - 1, 1);
|
|
90193
90783
|
maxId = Math.min((N - bestS) + 1, N);
|
|
90194
90784
|
}
|
|
90195
|
-
|
|
90196
|
-
// Sort the array values, and subset it to those districts
|
|
90197
|
-
// NOTE - I'm *not* keeping track of the district indexes right now
|
|
90198
|
-
let subsetVfArray = VfArray.sort().slice(minId - 1, maxId);
|
|
90199
|
-
// Est. competitive districts on that array
|
|
90200
|
-
const Md = U.sumArray(subsetVfArray.map(v => estDistrictCompetitiveness(v)));
|
|
90201
|
-
// Est. competitive district share on that result
|
|
90202
|
-
const MdShare = U.trim(estCompetitiveDistrictsShare(Md, deltaS));
|
|
90203
|
-
return U.trim(MdShare);
|
|
90204
|
-
}
|
|
90205
|
-
exports.estMarginalCompetitiveShare = estMarginalCompetitiveShare;
|
|
90206
|
-
|
|
90207
|
-
|
|
90208
|
-
/***/ }),
|
|
90209
|
-
|
|
90210
|
-
/***/ "./src/index.ts":
|
|
90211
|
-
/*!**********************!*\
|
|
90212
|
-
!*** ./src/index.ts ***!
|
|
90213
|
-
\**********************/
|
|
90214
|
-
/*! no static exports found */
|
|
90215
|
-
/***/ (function(module, exports, __webpack_require__) {
|
|
90216
|
-
|
|
90217
|
-
"use strict";
|
|
90218
|
-
|
|
90219
|
-
//
|
|
90220
|
-
// SCORE PACKAGE API
|
|
90221
|
-
//
|
|
90222
|
-
function __export(m) {
|
|
90223
|
-
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
|
90224
|
-
}
|
|
90225
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90226
|
-
__export(__webpack_require__(/*! ./_api */ "./src/_api.ts"));
|
|
90227
|
-
var types_1 = __webpack_require__(/*! ./types */ "./src/types.ts");
|
|
90228
|
-
exports.sampleProfile = types_1.sampleProfile;
|
|
90229
|
-
exports.sampleScorecard = types_1.sampleScorecard;
|
|
90230
|
-
|
|
90231
|
-
|
|
90232
|
-
/***/ }),
|
|
90233
|
-
|
|
90234
|
-
/***/ "./src/normalize.ts":
|
|
90235
|
-
/*!**************************!*\
|
|
90236
|
-
!*** ./src/normalize.ts ***!
|
|
90237
|
-
\**************************/
|
|
90238
|
-
/*! no static exports found */
|
|
90239
|
-
/***/ (function(module, exports, __webpack_require__) {
|
|
90240
|
-
|
|
90241
|
-
"use strict";
|
|
90242
|
-
|
|
90243
|
-
//
|
|
90244
|
-
// NORMALIZATION UTILITIES
|
|
90245
|
-
//
|
|
90246
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
90247
|
-
if (mod && mod.__esModule) return mod;
|
|
90248
|
-
var result = {};
|
|
90249
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
90250
|
-
result["default"] = mod;
|
|
90251
|
-
return result;
|
|
90252
|
-
};
|
|
90253
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90254
|
-
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
90255
|
-
class Normalizer {
|
|
90256
|
-
constructor(rawValue) {
|
|
90257
|
-
this.rawNum = rawValue;
|
|
90258
|
-
this.wipNum = this.rawNum;
|
|
90259
|
-
}
|
|
90260
|
-
// *Don't* transform the input.
|
|
90261
|
-
identity() {
|
|
90262
|
-
return this.wipNum;
|
|
90263
|
-
}
|
|
90264
|
-
// TODO - Do I need this? If so, add TEST cases.
|
|
90265
|
-
positive() {
|
|
90266
|
-
this.wipNum = Math.abs(this.wipNum);
|
|
90267
|
-
return this.wipNum;
|
|
90268
|
-
}
|
|
90269
|
-
// Invert a value in the unit range [0.0–1.0] (so that bigger is better).
|
|
90270
|
-
invert() {
|
|
90271
|
-
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
90272
|
-
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), S.OUT_OF_RANGE_MSG);
|
|
90273
|
-
this.wipNum = 1.0 - this.wipNum;
|
|
90274
|
-
return this.wipNum;
|
|
90275
|
-
}
|
|
90276
|
-
// Constrain the value to be within a specified range.
|
|
90277
|
-
clip(begin_range, end_range) {
|
|
90278
|
-
// Handle the ends of the range being given either order
|
|
90279
|
-
const min_range = Math.min(begin_range, end_range);
|
|
90280
|
-
const max_range = Math.max(begin_range, end_range);
|
|
90281
|
-
this.wipNum = Math.max(Math.min(this.wipNum, max_range), min_range);
|
|
90282
|
-
return this.wipNum;
|
|
90283
|
-
}
|
|
90284
|
-
// Recast the value as the delta from a baseline value. NOOP if it is zero.
|
|
90285
|
-
// NOTE - Values can be + or -.
|
|
90286
|
-
rebase(base) {
|
|
90287
|
-
this.wipNum = this.wipNum - base;
|
|
90288
|
-
return this.wipNum;
|
|
90289
|
-
}
|
|
90290
|
-
// Re-scale a value into the [0.0 – 1.0] range, using a given range.
|
|
90291
|
-
// NOTE - This assumes that values have alrady been clipped into the range.
|
|
90292
|
-
unitize(begin_range, end_range) {
|
|
90293
|
-
const min_range = Math.min(begin_range, end_range);
|
|
90294
|
-
const max_range = Math.max(begin_range, end_range);
|
|
90295
|
-
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
90296
|
-
console.assert(((this.wipNum >= min_range) && (this.wipNum <= max_range)), S.OUT_OF_RANGE_MSG);
|
|
90297
|
-
const ranged = this.wipNum - min_range;
|
|
90298
|
-
this.wipNum = Math.abs(ranged / (end_range - begin_range));
|
|
90299
|
-
return this.wipNum;
|
|
90300
|
-
}
|
|
90301
|
-
// Decay a value in the unit range [0.0–1.0] by its distance from zero.
|
|
90302
|
-
// NOTE - If the range is already such that "bigger is better," then the closer
|
|
90303
|
-
// the value is to 1.0 (the best) the *less* it will decay.
|
|
90304
|
-
decay(fn = 0 /* Gravity */) {
|
|
90305
|
-
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
90306
|
-
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), S.OUT_OF_RANGE_MSG);
|
|
90307
|
-
switch (fn) {
|
|
90308
|
-
case 0 /* Gravity */: {
|
|
90309
|
-
this.wipNum = Math.pow(this.wipNum, S.DISTANCE_WEIGHT);
|
|
90310
|
-
return this.wipNum;
|
|
90311
|
-
}
|
|
90312
|
-
default: {
|
|
90313
|
-
throw new Error("Decay function not recognized.");
|
|
90314
|
-
}
|
|
90315
|
-
}
|
|
90316
|
-
}
|
|
90317
|
-
// Translate a value in the unit range to the user-friendly range [0 – 100].
|
|
90318
|
-
rescale() {
|
|
90319
|
-
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
90320
|
-
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), S.OUT_OF_RANGE_MSG);
|
|
90321
|
-
this.normalizedNum = Math.round(this.wipNum * S.NORMALIZED_RANGE);
|
|
90322
|
-
return this.normalizedNum;
|
|
90323
|
-
}
|
|
90785
|
+
return [minId, maxId];
|
|
90324
90786
|
}
|
|
90325
|
-
exports.
|
|
90787
|
+
exports.findMarginalDistricts = findMarginalDistricts;
|
|
90326
90788
|
|
|
90327
90789
|
|
|
90328
90790
|
/***/ }),
|
|
@@ -90347,74 +90809,111 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
90347
90809
|
return result;
|
|
90348
90810
|
};
|
|
90349
90811
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90812
|
+
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
90350
90813
|
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
90814
|
+
const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
90351
90815
|
const compact_1 = __webpack_require__(/*! ./compact */ "./src/compact.ts");
|
|
90352
90816
|
const cohesive_1 = __webpack_require__(/*! ./cohesive */ "./src/cohesive.ts");
|
|
90353
90817
|
const equal_1 = __webpack_require__(/*! ./equal */ "./src/equal.ts");
|
|
90354
|
-
const
|
|
90355
|
-
|
|
90356
|
-
function scorePlan(p, overridesJSON
|
|
90357
|
-
//
|
|
90818
|
+
const partisan_1 = __webpack_require__(/*! ./partisan */ "./src/partisan.ts");
|
|
90819
|
+
const minority_1 = __webpack_require__(/*! ./minority */ "./src/minority.ts");
|
|
90820
|
+
function scorePlan(p, overridesJSON) {
|
|
90821
|
+
// TRADITIONAL DISTRICTING PRINCIPLES ("best") subcategories - compactness, splitting, population deviation
|
|
90358
90822
|
// Compactness
|
|
90359
|
-
const reockM = compact_1.doReock(p.compactnessProfile.
|
|
90360
|
-
const polsbyM = compact_1.doPolsbyPopper(p.compactnessProfile.
|
|
90361
|
-
const compactnessScore =
|
|
90823
|
+
const reockM = compact_1.doReock(p.compactnessProfile.geometryByDistrict);
|
|
90824
|
+
const polsbyM = compact_1.doPolsbyPopper(p.compactnessProfile.geometryByDistrict);
|
|
90825
|
+
const compactnessScore = weightCompactness(reockM.normalized, polsbyM.normalized);
|
|
90362
90826
|
const cS = {
|
|
90363
90827
|
score: compactnessScore,
|
|
90364
90828
|
reock: reockM,
|
|
90365
90829
|
polsby: polsbyM
|
|
90366
90830
|
};
|
|
90367
90831
|
// Splitting
|
|
90368
|
-
const CxD = p.splittingProfile.
|
|
90832
|
+
const CxD = p.splittingProfile.countyPopByDistrict;
|
|
90369
90833
|
const dT = cohesive_1.totalDistricts(CxD);
|
|
90370
90834
|
const cT = cohesive_1.totalCounties(CxD);
|
|
90371
90835
|
const countyM = cohesive_1.doCountySplittingReduced(CxD, dT, cT);
|
|
90372
90836
|
const districtM = cohesive_1.doDistrictSplittingReduced(CxD, dT, cT);
|
|
90373
|
-
const splittingScore =
|
|
90837
|
+
const splittingScore = weightSplitting(countyM.normalized, districtM.normalized);
|
|
90374
90838
|
const sS = {
|
|
90375
90839
|
score: splittingScore,
|
|
90376
90840
|
county: countyM,
|
|
90377
90841
|
district: districtM
|
|
90378
90842
|
};
|
|
90379
90843
|
// Population deviation
|
|
90380
|
-
const
|
|
90381
|
-
// Combine
|
|
90382
|
-
const
|
|
90383
|
-
// Populate the "best" scorecard
|
|
90384
|
-
const
|
|
90385
|
-
|
|
90844
|
+
const pdS = equal_1.doPopulationDeviation(p.populationProfile.totalPopByDistrict, p.populationProfile.targetSize, p.legislativeDistricts);
|
|
90845
|
+
// Combine traditional principles into a score to compare plans w/in a state
|
|
90846
|
+
const tpScore = weightTradtionalPrinciples(cS.score, sS.score, pdS.normalized, 1 /* WithinAState */);
|
|
90847
|
+
// Populate the "best" traditional principles scorecard
|
|
90848
|
+
const tpS = {
|
|
90849
|
+
score: tpScore,
|
|
90386
90850
|
compactness: cS,
|
|
90387
90851
|
splitting: sS,
|
|
90388
|
-
populationDeviation:
|
|
90852
|
+
populationDeviation: pdS
|
|
90389
90853
|
};
|
|
90390
|
-
//
|
|
90391
|
-
|
|
90392
|
-
|
|
90393
|
-
|
|
90394
|
-
|
|
90395
|
-
const
|
|
90854
|
+
// PARTISAN ("fair") subcategories - bias, impact, & competitiveness (plus lots of supporting measures)
|
|
90855
|
+
const pS = partisan_1.scorePartisan(p.partisanProfile.statewideVf, p.partisanProfile.vfArray);
|
|
90856
|
+
// Combine the partisan/partisan & traditional principles/best scores into an overall
|
|
90857
|
+
// score for comparing plans w/in a state
|
|
90858
|
+
let score = weightOverall(pS.score2, tpS.score, 1 /* WithinAState */);
|
|
90859
|
+
const mS = minority_1.evalMinorityOpportunity(p);
|
|
90860
|
+
// Add minority bonus, keeping score it to the range [0–100]
|
|
90861
|
+
score = mixinMinorityBonus(score, mS.score);
|
|
90396
90862
|
// Roll up an overall scorecard
|
|
90397
90863
|
const scorecard = {
|
|
90398
|
-
|
|
90399
|
-
|
|
90400
|
-
|
|
90864
|
+
partisan: pS,
|
|
90865
|
+
minority: mS,
|
|
90866
|
+
traditionalPrinciples: tpS,
|
|
90867
|
+
score: score
|
|
90401
90868
|
};
|
|
90402
90869
|
return scorecard;
|
|
90403
90870
|
}
|
|
90404
90871
|
exports.scorePlan = scorePlan;
|
|
90872
|
+
// HELPERS
|
|
90873
|
+
function weightCompactness(rS, ppS) {
|
|
90874
|
+
const rW = C.reockWeight();
|
|
90875
|
+
const ppW = C.polsbyWeight();
|
|
90876
|
+
const score = Math.round(((rS * rW) + (ppS * ppW)) / (rW + ppW));
|
|
90877
|
+
return score;
|
|
90878
|
+
}
|
|
90879
|
+
exports.weightCompactness = weightCompactness;
|
|
90880
|
+
function weightSplitting(csS, dsS) {
|
|
90881
|
+
const csW = C.countySplittingWeight();
|
|
90882
|
+
const dsW = C.districtSplittingWeight();
|
|
90883
|
+
const score = Math.round(((csS * csW) + (dsS * dsW)) / (csW + dsW));
|
|
90884
|
+
return score;
|
|
90885
|
+
}
|
|
90886
|
+
exports.weightSplitting = weightSplitting;
|
|
90887
|
+
function weightTradtionalPrinciples(cS, sS, pdS, context) {
|
|
90888
|
+
const cW = C.compactnessWeight(context);
|
|
90889
|
+
const sW = C.splittingWeight(context);
|
|
90890
|
+
const pdW = C.popdevWeight(context);
|
|
90891
|
+
const score = Math.round(((cS * cW) + (sS * sW) + (pdS * pdW)) / (cW + sW + pdW));
|
|
90892
|
+
return score;
|
|
90893
|
+
}
|
|
90894
|
+
exports.weightTradtionalPrinciples = weightTradtionalPrinciples;
|
|
90895
|
+
function weightOverall(pS, tpS, context) {
|
|
90896
|
+
const pW = C.partisanWeight(context);
|
|
90897
|
+
const tpW = C.traditionalPrinciplesWeight(context);
|
|
90898
|
+
const score = Math.round(((pS * pW) + (tpS * tpW)) / (pW + tpW));
|
|
90899
|
+
return score;
|
|
90900
|
+
}
|
|
90901
|
+
exports.weightOverall = weightOverall;
|
|
90902
|
+
function mixinMinorityBonus(score, minorityBonus) {
|
|
90903
|
+
const modifiedScore = Math.min(score + minorityBonus, S.NORMALIZED_RANGE);
|
|
90904
|
+
return modifiedScore;
|
|
90905
|
+
}
|
|
90906
|
+
exports.mixinMinorityBonus = mixinMinorityBonus;
|
|
90907
|
+
function printScorecardHeader() {
|
|
90908
|
+
console.log('XX, Name, N, V%, ^S#, S#, B%, B$, UE#, I$, C#, Cd, Md, C$, <P$, >P$, Rc, Rc$, Pc, Pc$, G$, Cs, Cs$, Ds, Ds$, S$, Eq, Eq$, T$, O1, O2, Od, Pd, M$, $$$');
|
|
90909
|
+
}
|
|
90910
|
+
exports.printScorecardHeader = printScorecardHeader;
|
|
90911
|
+
function printScorecardRow(xx, name, N, Vf, s) {
|
|
90912
|
+
console.log('%s, %s, %i, %f, %i, %f, %f, %i, %f, %i, %i, %f, %f, %i, %i, %i, %f, %i, %f, %i, %i, %f, %i, %f, %i, %i, %f, %i, %i, %i, %i, %f, %i, %i, %i', xx, name, N, U.trim(Vf), s.partisan.bias.bestS, s.partisan.bias.probableS, s.partisan.bias.bias, s.partisan.bias.score, s.partisan.impact.unearnedS, s.partisan.impact.score, s.partisan.competitiveness.c, s.partisan.competitiveness.cD, s.partisan.competitiveness.mD, s.partisan.competitiveness.score, s.partisan.score, s.partisan.score2, 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.nOpportunity1, s.minority.nOpportunity2, s.minority.opportunityDistricts, s.minority.nProportional, s.minority.score, s.score);
|
|
90913
|
+
}
|
|
90914
|
+
exports.printScorecardRow = printScorecardRow;
|
|
90405
90915
|
|
|
90406
90916
|
|
|
90407
|
-
/***/ }),
|
|
90408
|
-
|
|
90409
|
-
/***/ "./src/scoring-defaults.json":
|
|
90410
|
-
/*!***********************************!*\
|
|
90411
|
-
!*** ./src/scoring-defaults.json ***!
|
|
90412
|
-
\***********************************/
|
|
90413
|
-
/*! exports provided: fair, best, default */
|
|
90414
|
-
/***/ (function(module) {
|
|
90415
|
-
|
|
90416
|
-
module.exports = JSON.parse("{\"fair\":{\"unbiased\":{\"worst\":0.1,\"best\":0,\"weight\":80},\"competitive\":{\"worst\":0,\"best\":0.67,\"weight\":20,\"min\":0.25,\"max\":0.75,\"marginal\":75,\"overall\":25},\"weight\":80},\"best\":{\"compact\":{\"reock\":{\"worst\":0.25,\"best\":0.5,\"weight\":50},\"polsby\":{\"worst\":0.1,\"best\":0.5,\"weight\":50},\"weight\":50},\"cohesive\":{\"county\":{\"best\":1,\"worst\":1.71,\"allowableSplitsMultiplier\":1.5,\"weight\":50},\"district\":{\"best\":1,\"worst\":1.5,\"weight\":50},\"weight\":50},\"equal\":{\"worst\":0.0075,\"best\":0.002,\"stateLeg\":{\"worst\":0.1},\"weight\":0},\"weight\":20}}");
|
|
90417
|
-
|
|
90418
90917
|
/***/ }),
|
|
90419
90918
|
|
|
90420
90919
|
/***/ "./src/settings.ts":
|
|
@@ -90429,16 +90928,13 @@ module.exports = JSON.parse("{\"fair\":{\"unbiased\":{\"worst\":0.1,\"best\":0,\
|
|
|
90429
90928
|
//
|
|
90430
90929
|
// GLOBAL CONSTANTS
|
|
90431
90930
|
//
|
|
90432
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
90433
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
90434
|
-
};
|
|
90435
90931
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90436
90932
|
// Normalized scores [0-100]
|
|
90437
90933
|
exports.NORMALIZED_RANGE = 100;
|
|
90438
90934
|
// Square deviations from the ideal
|
|
90439
90935
|
exports.DISTANCE_WEIGHT = 2;
|
|
90440
90936
|
// Out of range message
|
|
90441
|
-
exports.OUT_OF_RANGE_MSG = "
|
|
90937
|
+
exports.OUT_OF_RANGE_MSG = "%f out of range";
|
|
90442
90938
|
// A small delta to use when testing ranges of values
|
|
90443
90939
|
exports.EPSILON = 1 / Math.pow(10, 6);
|
|
90444
90940
|
// Keep 4 decimals for fractions [0.0–1.0], i.e., 2 for %'s [0–100]
|
|
@@ -90446,68 +90942,6 @@ exports.PRECISION = 4;
|
|
|
90446
90942
|
// "Roughly equal" = average census block size / 2
|
|
90447
90943
|
exports.AVERAGE_BLOCK_SIZE = 30;
|
|
90448
90944
|
exports.EQUAL_TOLERANCE = exports.AVERAGE_BLOCK_SIZE / 2;
|
|
90449
|
-
// SCORING PARAMETERS
|
|
90450
|
-
const scoring_defaults_json_1 = __importDefault(__webpack_require__(/*! ./scoring-defaults.json */ "./src/scoring-defaults.json"));
|
|
90451
|
-
function readOverrides() {
|
|
90452
|
-
// TODO - TERRY: Replumb this to:
|
|
90453
|
-
// - Respond to an environment variable or config.json, and
|
|
90454
|
-
// - Read settings.json at runtime from S3
|
|
90455
|
-
// - Otherwise return false/null, so the conditionals below work
|
|
90456
|
-
// - Note the utils/settings interdependency
|
|
90457
|
-
// return U.readJSON('scoring-overrides.json');
|
|
90458
|
-
return undefined;
|
|
90459
|
-
}
|
|
90460
|
-
exports.readOverrides = readOverrides;
|
|
90461
|
-
let overrides = readOverrides();
|
|
90462
|
-
// SETTINGS FOR PARTISAN SCORING
|
|
90463
|
-
exports.MIN_CONTESTED = overrides ? overrides.fair.competitive.min : scoring_defaults_json_1.default.fair.competitive.min;
|
|
90464
|
-
exports.MAX_CONTESTED = overrides ? overrides.fair.competitive.max : scoring_defaults_json_1.default.fair.competitive.max;
|
|
90465
|
-
// SCALES FOR NORMALIZING RAW VALUES
|
|
90466
|
-
// Compactness scales
|
|
90467
|
-
exports.REOCK_WORST = overrides ? overrides.best.compact.reock.worst : scoring_defaults_json_1.default.best.compact.reock.worst;
|
|
90468
|
-
exports.REOCK_BEST = overrides ? overrides.best.compact.reock.best : scoring_defaults_json_1.default.best.compact.reock.best;
|
|
90469
|
-
exports.POLSBY_WORST = overrides ? overrides.best.compact.polsby.worst : scoring_defaults_json_1.default.best.compact.polsby.worst;
|
|
90470
|
-
exports.POLSBY_BEST = overrides ? overrides.best.compact.polsby.best : scoring_defaults_json_1.default.best.compact.polsby.best;
|
|
90471
|
-
// County-District splitting scales (not inverted)
|
|
90472
|
-
exports.COUNTY_BEST = overrides ? overrides.best.cohesive.county.best : scoring_defaults_json_1.default.best.cohesive.county.best;
|
|
90473
|
-
exports.COUNTY_WORST = overrides ? overrides.best.cohesive.county.worst : scoring_defaults_json_1.default.best.cohesive.county.worst;
|
|
90474
|
-
exports.ALLOWABLE_SPLITS_MULTIPLIER = overrides ? overrides.best.cohesive.county.allowableSplitsMultiplier : scoring_defaults_json_1.default.best.cohesive.county.allowableSplitsMultiplier;
|
|
90475
|
-
exports.DISTRICT_BEST = overrides ? overrides.best.cohesive.district.best : scoring_defaults_json_1.default.best.cohesive.district.best;
|
|
90476
|
-
exports.DISTRICT_WORST = overrides ? overrides.best.cohesive.district.worst : scoring_defaults_json_1.default.best.cohesive.district.worst;
|
|
90477
|
-
// Population deviation thresholds & scales (inverted)
|
|
90478
|
-
exports.POPDEV_WORST = overrides ? overrides.best.equal.worst : scoring_defaults_json_1.default.best.equal.worst;
|
|
90479
|
-
exports.POPDEV_BEST = overrides ? overrides.best.equal.best : scoring_defaults_json_1.default.best.equal.best;
|
|
90480
|
-
exports.POPEQ_MIN = 1.0 - exports.POPDEV_WORST;
|
|
90481
|
-
exports.POPEQ_MAX = 1.0 - exports.POPDEV_BEST;
|
|
90482
|
-
exports.POPDEV_WORST_LD = overrides ? overrides.best.equal.stateLeg.worst : scoring_defaults_json_1.default.best.equal.stateLeg.worst;
|
|
90483
|
-
exports.POPDEV_BEST_LD = ((exports.POPDEV_BEST / exports.POPDEV_WORST) * exports.POPDEV_WORST_LD);
|
|
90484
|
-
exports.POPEQ_MIN_LD = 1.0 - exports.POPDEV_WORST_LD;
|
|
90485
|
-
exports.POPEQ_MAX_LD = 1.0 - exports.POPDEV_BEST_LD;
|
|
90486
|
-
// Bias & competitiveness scales
|
|
90487
|
-
// TODO - SCORE:
|
|
90488
|
-
// * How wide a range do we want for bias? 10%? 20%?
|
|
90489
|
-
// * Do we want it to be state-specific? E.g., one seat?
|
|
90490
|
-
exports.BIAS_WORST = overrides ? overrides.fair.unbiased.worst : scoring_defaults_json_1.default.fair.unbiased.worst;
|
|
90491
|
-
exports.BIAS_BEST = overrides ? overrides.fair.unbiased.best : scoring_defaults_json_1.default.fair.unbiased.best;
|
|
90492
|
-
// The range for raw overall competitiveness values
|
|
90493
|
-
exports.COMPETITIVE_WORST = overrides ? overrides.fair.competitive.worst : scoring_defaults_json_1.default.fair.competitive.worst;
|
|
90494
|
-
exports.COMPETITIVE_BEST = overrides ? overrides.fair.competitive.best : scoring_defaults_json_1.default.fair.competitive.best;
|
|
90495
|
-
// WEIGHTS FOR COMBINING NORMALIZED VALUES
|
|
90496
|
-
exports.REOCK_WEIGHT = overrides ? overrides.best.compact.reock.weight : scoring_defaults_json_1.default.best.compact.reock.weight;
|
|
90497
|
-
exports.POLSBY_WEIGHT = overrides ? overrides.best.compact.polsby.weight : scoring_defaults_json_1.default.best.compact.polsby.weight;
|
|
90498
|
-
exports.COUNTY_WEIGHT = overrides ? overrides.best.cohesive.county.weight : scoring_defaults_json_1.default.best.cohesive.county.weight;
|
|
90499
|
-
exports.DISTRICT_WEIGHT = overrides ? overrides.best.cohesive.district.weight : scoring_defaults_json_1.default.best.cohesive.district.weight;
|
|
90500
|
-
exports.COMPACTNESS_WEIGHT = overrides ? overrides.best.compact.weight : scoring_defaults_json_1.default.best.compact.weight;
|
|
90501
|
-
exports.SPLITTING_WEIGHT = overrides ? overrides.best.cohesive.weight : scoring_defaults_json_1.default.best.cohesive.weight;
|
|
90502
|
-
exports.POPDEV_WEIGHT = overrides ? overrides.best.equal.weight : scoring_defaults_json_1.default.best.equal.weight;
|
|
90503
|
-
// TODO - SCORE: What kind of relative weighting do we want between marginal and
|
|
90504
|
-
// overall competitiveness?
|
|
90505
|
-
exports.MARGINAL_WEIGHT = overrides ? overrides.fair.competitive.marginal : scoring_defaults_json_1.default.fair.competitive.marginal;
|
|
90506
|
-
exports.OVERALL_WEIGHT = overrides ? overrides.fair.competitive.overall : scoring_defaults_json_1.default.fair.competitive.overall;
|
|
90507
|
-
exports.BIAS_WEIGHT = overrides ? overrides.fair.unbiased.weight : scoring_defaults_json_1.default.fair.unbiased.weight;
|
|
90508
|
-
exports.COMPETITIVE_WEIGHT = overrides ? overrides.fair.competitive.weight : scoring_defaults_json_1.default.fair.competitive.weight;
|
|
90509
|
-
exports.FAIR_WEIGHT = overrides ? overrides.fair.weight : scoring_defaults_json_1.default.fair.weight;
|
|
90510
|
-
exports.BEST_WEIGHT = overrides ? overrides.best.weight : scoring_defaults_json_1.default.best.weight;
|
|
90511
90945
|
|
|
90512
90946
|
|
|
90513
90947
|
/***/ }),
|
|
@@ -90528,9 +90962,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
90528
90962
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
90529
90963
|
};
|
|
90530
90964
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90531
|
-
const sample_profile_json_1 = __importDefault(__webpack_require__(/*! ../testdata/
|
|
90965
|
+
const sample_profile_json_1 = __importDefault(__webpack_require__(/*! ../testdata/samples/sample-profile.json */ "./testdata/samples/sample-profile.json"));
|
|
90532
90966
|
exports.sampleProfile = sample_profile_json_1.default;
|
|
90533
|
-
const sample_scorecard_json_1 = __importDefault(__webpack_require__(/*! ../testdata/
|
|
90967
|
+
const sample_scorecard_json_1 = __importDefault(__webpack_require__(/*! ../testdata/samples/sample-scorecard.json */ "./testdata/samples/sample-scorecard.json"));
|
|
90534
90968
|
exports.sampleScorecard = sample_scorecard_json_1.default;
|
|
90535
90969
|
|
|
90536
90970
|
|
|
@@ -90612,25 +91046,25 @@ exports.deepCopy = deepCopy;
|
|
|
90612
91046
|
|
|
90613
91047
|
/***/ }),
|
|
90614
91048
|
|
|
90615
|
-
/***/ "./testdata/
|
|
90616
|
-
|
|
90617
|
-
!*** ./testdata/
|
|
90618
|
-
|
|
91049
|
+
/***/ "./testdata/samples/sample-profile.json":
|
|
91050
|
+
/*!**********************************************!*\
|
|
91051
|
+
!*** ./testdata/samples/sample-profile.json ***!
|
|
91052
|
+
\**********************************************/
|
|
90619
91053
|
/*! exports provided: state, planName, nDistricts, nCounties, legislativeDistricts, populationProfile, compactnessProfile, splittingProfile, partisanProfile, demographicProfile, default */
|
|
90620
91054
|
/***/ (function(module) {
|
|
90621
91055
|
|
|
90622
|
-
module.exports = JSON.parse("{\"state\":\"NC\",\"planName\":\"
|
|
91056
|
+
module.exports = JSON.parse("{\"state\":\"NC\",\"planName\":\"NC 116th Congressional\",\"nDistricts\":13,\"nCounties\":100,\"legislativeDistricts\":false,\"populationProfile\":{\"totalPopByDistrict\":[733499,733499,733498,733499,733499,733498,733499,733499,733498,733499,733499,733498,733499],\"targetSize\":733499},\"compactnessProfile\":{\"geometryByDistrict\":[[1.577393519870778,10.159603194222512,2.6574239327900613],[0.7058443860242223,7.806818205304456,1.7632772181381422],[2.9200833695730037,9.972779463090355,2.733997011824872],[0.1913918462294299,3.5460738146162076,0.9224411598627837],[1.039017192939366,6.4920493759205495,2.1060599512212406],[1.0334601456545682,6.466840626614781,1.6394147493749138],[1.6241884494008865,7.988593581066711,1.9529170345835551],[0.7706777049448708,6.980189925133433,2.188634395661598],[0.9976766364327585,8.350880983965949,2.5604199958307654],[0.6762837622380331,5.8543721706585545,1.7264608756890525],[1.719230725216499,10.37139670690812,3.1586859214584764],[0.11329839503948308,2.2954988975477066,0.5379072920877442],[0.48371245467321944,5.3384017624136435,1.4517375375301198]]},\"splittingProfile\":{\"countyPopByDistrict\":[[0,0,0,0,0,0,0,21282,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240299,56552,0,0,0,12197,0,59916,0,0,54691,0,0,0,24669,0,0,0,0,0,0,0,0,0,0,0,0,24505,0,0,0,0,0,0,22099,0,0,0,0,0,0,0,80880,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45422,0,20972,13228,0,0,0,56787,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60619,0,0,0,0,0,0,0,114678,0,0,0,0,0,0,0,109332,0,0,0,0,0,0,0,0,0,0,0,0,95840,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,328583,0,0,0,0,0,24447,0,0],[0,0,0,0,0,0,47759,0,0,0,0,0,0,0,9980,66469,0,0,0,0,14793,0,0,0,103505,0,23547,33920,0,0,0,0,0,0,0,0,0,0,0,21362,0,0,0,0,0,0,0,5810,0,0,0,10153,0,59495,0,0,0,0,0,0,0,0,0,0,0,0,177772,0,13144,40661,0,13453,0,87268,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4407,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27288,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133801,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,572410,0,0,0,0,0,0,0,0],[0,37198,11155,0,27281,17797,0,0,0,0,0,0,0,0,0,0,0,9499,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,350670,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47401,73673,0,0,0,0,0,0,0,0,51079,0,69340,0,38406,0],[151131,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23719,0,63505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,162418,0,0,0,0,0,0,0,0,0,0,0,57866,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39464,0,0,141752,0,0,93643,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,8981,107431,0,0,0,0,0,0,0,0,0,0,0,0,0,58098,0,0,0,0,0,0,58505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59546,0,0,0,0,0,0,0,0,0,0,0,0,0,202667,0,0,0,0,0,52217,0,0,0,0,0,0,0,0,0,0,63431,0,0,0,0,0,0,0,0,0,0,0,0,0,122623,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,178011,0,0,0,0,0,0,0,0,0,0,0,0,243476,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46952,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27798,88247,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,88430,0,0,0,60585,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,26948,0,0,0,0,26209,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75955,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,186130,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46639,134168,0,0,0,0,36157,0,0,0,0,0,0,201292,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,111849,0,0,0,0,0,0,144859,0,0,0,0,98078,0,0,0,0,0,0,0,0,0,0,0,0,206086,0,0,0,0,0,0,0,0,0,0,0,0,6042,0,0,0,0,0,78265,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20510,0,0,0,0,0,67810,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,126469,90912,0,83029,0,0,0,0,0,27444,0,10587,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8861,0,0,0,0,0,59036,106740,0,0,0,0,40271,0,0,0,0,0,44996,33922,20764,0,0,15579,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13981,33090,0,0,0,0,0,0,0,0,0,0,0,17818],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,733498,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,162878,41240,0,0,0,0,0,0,0,0,0,0,325988,0,0,0,0,0,0,0,153395,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49998,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]},\"partisanProfile\":{\"statewideVf\":0.488799,\"vfArray\":[0.38133475250631976,0.3849820112175976,0.3932778243643098,0.4232536822067312,0.42479562842958446,0.43309446651496813,0.43760621455968074,0.43980028165892326,0.4545677480810613,0.4642133701735491,0.6853270858231137,0.6938881738049054,0.6953207231271951]},\"demographicProfile\":{\"stateMfArray\":[0.684371246819619,0.31562875318038097,0.21168281993226215,0.06787156278984616,0.0012540930000187486,0.024136155044881008,0.017483272326632705],\"mfArrayByDistrict\":[[0.46201994876162167,0.5379800512383783,0.44446912914151915,0.065885219684988,0.0009221497550043815,0.0218600624522836,0.01298238746863721],[0.7062964810448031,0.2937035189551968,0.19676408791341676,0.07140508797387993,0.0009787472035794184,0.02027027027027027,0.011283934941653062],[0.7125415968218078,0.2874584031781921,0.21181863608495496,0.05374539732717163,0.002106863519897368,0.01628256424250371,0.010404964330393058],[0.6162003899079305,0.3837996100920695,0.22386087117385056,0.08687191382180923,0.0012060108434249284,0.07153885560599377,0.010091002234143982],[0.7766842620090415,0.22331573799095852,0.14015703622760559,0.06690518783542039,0.0007790124871119258,0.013001753659331847,0.007680851626320752],[0.7111584046318987,0.2888415953681012,0.19851813634865356,0.07148666466027488,0.0008061395588804333,0.013621967123837368,0.010186021181764765],[0.7086112059348308,0.2913887940651691,0.20229594619799895,0.06953648210647081,0.001149028868250555,0.009700759801937688,0.014166663733974303],[0.6697822118227867,0.33021778817721326,0.2240088599271958,0.07249565635824667,0.002909229825482943,0.022877125445843145,0.01884938491094157],[0.6406346877484486,0.35936531225155144,0.19622106825202063,0.059419121439330605,0.0010389455633486816,0.02217720538538736,0.08616917683114156],[0.8192767260107778,0.18072327398922214,0.11582505713398979,0.045060025957170666,0.0006806703721468273,0.014117738340433936,0.008000521964844963],[0.8956213645578734,0.1043786354421266,0.032662784268536554,0.04329373425036473,0.0012436075643032956,0.010267513422177209,0.019651410943402076],[0.46574079494234033,0.5342592050576597,0.36183998712169996,0.12151883451384417,0.001650032195750161,0.050651598079962536,0.010997775566352515],[0.6979595109954735,0.3020404890045265,0.21171009017357523,0.0572352710553516,0.0008767865416830024,0.028566846063370996,0.009525252165235058]]}}");
|
|
90623
91057
|
|
|
90624
91058
|
/***/ }),
|
|
90625
91059
|
|
|
90626
|
-
/***/ "./testdata/
|
|
90627
|
-
|
|
90628
|
-
!*** ./testdata/
|
|
90629
|
-
|
|
90630
|
-
/*! exports provided: score,
|
|
91060
|
+
/***/ "./testdata/samples/sample-scorecard.json":
|
|
91061
|
+
/*!************************************************!*\
|
|
91062
|
+
!*** ./testdata/samples/sample-scorecard.json ***!
|
|
91063
|
+
\************************************************/
|
|
91064
|
+
/*! exports provided: score, partisan, minority, traditionalPrinciples, default */
|
|
90631
91065
|
/***/ (function(module) {
|
|
90632
91066
|
|
|
90633
|
-
module.exports = JSON.parse("{\"score\":5,\"
|
|
91067
|
+
module.exports = JSON.parse("{\"score\":5,\"partisan\":{\"bias\":{\"bestS\":7,\"bestSf\":0.5385,\"probableS\":4.1925,\"probableSf\":0.3225,\"bias\":0.216,\"score\":0},\"impact\":{\"unearnedS\":2.8075,\"score\":0},\"competitiveness\":{\"c\":6,\"cD\":0.7266,\"cDf\":0.0559,\"mRange\":[5,11],\"mD\":0.7134,\"mDf\":0.1019,\"score\":10},\"score\":3,\"score2\":2},\"minority\":{\"report\":{\"bucketsByDemographic\":[[10,14],[3,0],[1,9],[0,0],[0,0],[0,0]],\"averageDVf\":0.6737588682747838},\"nOpportunity1\":4,\"nOpportunity2\":9,\"nProportional\":18,\"opportunityDistricts\":12.9424,\"score\":14},\"traditionalPrinciples\":{\"score\":18,\"compactness\":{\"score\":35,\"reock\":{\"raw\":0.3373,\"normalized\":35,\"notes\":{}},\"polsby\":{\"raw\":0.2418,\"normalized\":35,\"notes\":{}}},\"splitting\":{\"score\":0,\"county\":{\"raw\":1.1474,\"normalized\":0,\"notes\":{}},\"district\":{\"raw\":1.4839,\"normalized\":3,\"notes\":{}}},\"populationDeviation\":{\"raw\":0.016,\"normalized\":0,\"notes\":{\"maxDeviation\":11693}}}}");
|
|
90634
91068
|
|
|
90635
91069
|
/***/ })
|
|
90636
91070
|
|
|
@@ -100416,6 +100850,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
100416
100850
|
return result;
|
|
100417
100851
|
};
|
|
100418
100852
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
100853
|
+
const Score = __importStar(__webpack_require__(/*! @dra2020/dra-score */ "./node_modules/@dra2020/dra-score/dist/dra-score.bundle.js"));
|
|
100419
100854
|
const preprocess_1 = __webpack_require__(/*! ./preprocess */ "./src/preprocess.ts");
|
|
100420
100855
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
100421
100856
|
const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
|
|
@@ -100443,37 +100878,47 @@ class AnalyticsSession {
|
|
|
100443
100878
|
this.districts = new D.Districts(this, SessionRequest['districtShapes']);
|
|
100444
100879
|
// TODO - SCORE: Toggle
|
|
100445
100880
|
if (this.useLegacy()) {
|
|
100881
|
+
console.log("Using legacy district-analytics.");
|
|
100446
100882
|
// NOTE: I've pulled these out of the individual analytics to here. Eventually,
|
|
100447
100883
|
// we could want them to passed into an analytics session as data, along with
|
|
100448
100884
|
// everything else. For now, this keeps branching out of the main code.
|
|
100449
100885
|
results_1.doConfigureScales(this);
|
|
100450
100886
|
}
|
|
100887
|
+
else {
|
|
100888
|
+
console.log("Using dra-score analytics.");
|
|
100889
|
+
// TODO - SCORE: Temporary HACK. Query dra-score for threshold.
|
|
100890
|
+
results_1.doConfigureScales(this);
|
|
100891
|
+
}
|
|
100451
100892
|
}
|
|
100452
100893
|
processConfig(config) {
|
|
100453
100894
|
// NOTE - Session settings are required:
|
|
100454
100895
|
// - Analytics suites can be defaulted to all with [], but
|
|
100455
100896
|
// - Dataset keys must be explicitly specified with 'dataset'
|
|
100897
|
+
// TODO - SCORE: Delete
|
|
100456
100898
|
config['suites'] = [0 /* Legal */, 1 /* Fair */, 2 /* Best */];
|
|
100457
100899
|
// Default the Census & redistricting cycle to 2010
|
|
100458
100900
|
if (!(U.keyExists('cycle', config)))
|
|
100459
100901
|
config['cycle'] = 2010;
|
|
100460
100902
|
return config;
|
|
100461
100903
|
}
|
|
100904
|
+
// TODO - SCORE: Toggle = Invert this logic
|
|
100462
100905
|
useLegacy() {
|
|
100463
|
-
|
|
100906
|
+
// TODO - SCORE: Opt-out
|
|
100907
|
+
return (U.keyExists('useScore', this.config) && (this.config['useScore'] != null) && (!(this.config['useScore']))) ? true : false;
|
|
100908
|
+
// TODO - SCORE: Opt-in
|
|
100909
|
+
// return (U.keyExists('useScore', this.config) && (this.config['useScore'])) ? false : true;
|
|
100464
100910
|
}
|
|
100465
100911
|
// Using the the data in the analytics session, calculate all the
|
|
100466
100912
|
// analytics & validations, saving/updating the individual test results.
|
|
100467
|
-
analyzePlan(bLog = false, overridesJSON
|
|
100913
|
+
analyzePlan(bLog = false, overridesJSON) {
|
|
100468
100914
|
try {
|
|
100469
100915
|
preprocess_1.doPreprocessData(this, bLog);
|
|
100470
100916
|
analyze_1.doAnalyzeDistricts(this, bLog);
|
|
100471
|
-
// TODO - SCORE
|
|
100472
100917
|
analyze_1.doAnalyzePlan(this, bLog);
|
|
100918
|
+
// TODO - SCORE
|
|
100473
100919
|
this._profile = score_1.profilePlan(this, bLog);
|
|
100474
|
-
this._scorecard = score_1.scorePlan(this._profile, bLog);
|
|
100920
|
+
this._scorecard = score_1.scorePlan(this, this._profile, bLog, overridesJSON);
|
|
100475
100921
|
results_1.doAnalyzePostProcessing(this, bLog);
|
|
100476
|
-
//
|
|
100477
100922
|
}
|
|
100478
100923
|
catch (_a) {
|
|
100479
100924
|
console.log("Exception caught by analyzePlan()");
|
|
@@ -100595,13 +101040,17 @@ class AnalyticsSession {
|
|
|
100595
101040
|
// NOTE - Not sure why this has to be up here ...
|
|
100596
101041
|
populationDeviationThreshold() {
|
|
100597
101042
|
// TODO - SCORE: Toggle
|
|
100598
|
-
// const bLegacy = this._bLegacy; DELETE
|
|
100599
101043
|
if (this.useLegacy()) {
|
|
100600
101044
|
return 1 - this.testScales[4 /* PopulationDeviation */]['scale'][0];
|
|
100601
101045
|
}
|
|
100602
101046
|
else {
|
|
100603
|
-
|
|
100604
|
-
|
|
101047
|
+
// NOTE - This assumes the plan has been profiled
|
|
101048
|
+
const scorer = new Score.Scorer();
|
|
101049
|
+
const popdev = scorer.populationDeviationThreshold(this.legislativeDistricts);
|
|
101050
|
+
return popdev;
|
|
101051
|
+
// TODO - SCORE: Temporary HACK. Query dra-score for threshold.
|
|
101052
|
+
// NOTE - The plan may not have been scored yet, i.e., no scorecard yet.
|
|
101053
|
+
// return 1 - this.testScales[T.Test.PopulationDeviation]['scale'][0];
|
|
100605
101054
|
}
|
|
100606
101055
|
}
|
|
100607
101056
|
}
|
|
@@ -100778,7 +101227,6 @@ class Districts {
|
|
|
100778
101227
|
let outerThis = this;
|
|
100779
101228
|
// Default the pop dev % for the dummy Unassigned district to 0%.
|
|
100780
101229
|
// Default the pop dev % for real (1–N) but empty districts to 100%.
|
|
100781
|
-
// TODO - SCORE: Why did I mark this?
|
|
100782
101230
|
let popDevPct = (i > 0) ? (targetSize / targetSize) : 0 / targetSize;
|
|
100783
101231
|
// Get the geoIDs assigned to the district
|
|
100784
101232
|
// Guard against empty districts
|
|
@@ -101228,12 +101676,44 @@ exports.doAnalyzeDistricts = doAnalyzeDistricts;
|
|
|
101228
101676
|
// calls might make chunking for aync easier.
|
|
101229
101677
|
function doAnalyzePlan(s, bLog = false) {
|
|
101230
101678
|
// TODO - SCORE: Toggle
|
|
101231
|
-
|
|
101232
|
-
|
|
101233
|
-
|
|
101234
|
-
|
|
101235
|
-
|
|
101236
|
-
|
|
101679
|
+
if (s.useLegacy()) {
|
|
101680
|
+
// Disable most legacy analytics
|
|
101681
|
+
// Get the requested suites, and only execute those tests
|
|
101682
|
+
let requestedSuites = s.config['suites'];
|
|
101683
|
+
// Tests in the "Legal" suite, i.e., pass/ fail constraints
|
|
101684
|
+
if (requestedSuites.includes(0 /* Legal */)) {
|
|
101685
|
+
s.tests[0 /* Complete */] = valid_1.doIsComplete(s, bLog);
|
|
101686
|
+
s.tests[1 /* Contiguous */] = valid_1.doIsContiguous(s, bLog);
|
|
101687
|
+
s.tests[2 /* FreeOfHoles */] = valid_1.doIsFreeOfHoles(s, bLog);
|
|
101688
|
+
s.tests[4 /* PopulationDeviation */] = equal_1.doPopulationDeviation(s, bLog);
|
|
101689
|
+
// NOTE - I can't check whether a population deviation is legal or not, until
|
|
101690
|
+
// the raw % is normalized. A zero (0) would mean "too much" / "not legal," for
|
|
101691
|
+
// the given type of district (CD vs. LD). The EqualPopulation test is derived
|
|
101692
|
+
// from PopulationDeviation, as part of scorecard or test log preparation.
|
|
101693
|
+
// Create an empty test entry here though ...
|
|
101694
|
+
s.tests[3 /* EqualPopulation */] = s.getTest(3 /* EqualPopulation */);
|
|
101695
|
+
}
|
|
101696
|
+
// Tests in the "Fair" suite
|
|
101697
|
+
if (requestedSuites.includes(1 /* Fair */)) {
|
|
101698
|
+
s.tests[11 /* SeatsBias */] = political_1.doSeatsBias(s, bLog);
|
|
101699
|
+
s.tests[12 /* VotesBias */] = political_1.doVotesBias(s, bLog);
|
|
101700
|
+
s.tests[13 /* Responsiveness */] = political_1.doResponsiveness(s, bLog);
|
|
101701
|
+
s.tests[14 /* ResponsiveDistricts */] = political_1.doResponsiveDistricts(s, bLog);
|
|
101702
|
+
s.tests[15 /* EfficiencyGap */] = political_1.doEfficiencyGap(s, bLog);
|
|
101703
|
+
s.tests[16 /* MajorityMinorityDistricts */] = minority_1.doMajorityMinorityDistricts(s, bLog);
|
|
101704
|
+
}
|
|
101705
|
+
// Tests in the "Best" suite, i.e., criteria for better/worse
|
|
101706
|
+
if (requestedSuites.includes(2 /* Best */)) {
|
|
101707
|
+
s.tests[5 /* Reock */] = compact_1.doReock(s, bLog);
|
|
101708
|
+
s.tests[6 /* PolsbyPopper */] = compact_1.doPolsbyPopper(s, bLog);
|
|
101709
|
+
s.tests[7 /* UnexpectedCountySplits */] = cohesive_1.doFindCountiesSplitUnexpectedly(s, bLog);
|
|
101710
|
+
s.tests[10 /* VTDSplits */] = cohesive_1.doFindSplitVTDs(s, bLog);
|
|
101711
|
+
s.tests[8 /* CountySplitting */] = cohesive_1.doCountySplitting(s, bLog);
|
|
101712
|
+
s.tests[9 /* DistrictSplitting */] = cohesive_1.doDistrictSplitting(s, bLog);
|
|
101713
|
+
}
|
|
101714
|
+
}
|
|
101715
|
+
else {
|
|
101716
|
+
// TODO - SCORE: Except these. Continue to do these here vs. dra-score.
|
|
101237
101717
|
s.tests[0 /* Complete */] = valid_1.doIsComplete(s, bLog);
|
|
101238
101718
|
s.tests[1 /* Contiguous */] = valid_1.doIsContiguous(s, bLog);
|
|
101239
101719
|
s.tests[2 /* FreeOfHoles */] = valid_1.doIsFreeOfHoles(s, bLog);
|
|
@@ -101244,24 +101724,8 @@ function doAnalyzePlan(s, bLog = false) {
|
|
|
101244
101724
|
// from PopulationDeviation, as part of scorecard or test log preparation.
|
|
101245
101725
|
// Create an empty test entry here though ...
|
|
101246
101726
|
s.tests[3 /* EqualPopulation */] = s.getTest(3 /* EqualPopulation */);
|
|
101247
|
-
}
|
|
101248
|
-
// Tests in the "Fair" suite
|
|
101249
|
-
if (requestedSuites.includes(1 /* Fair */)) {
|
|
101250
|
-
s.tests[11 /* SeatsBias */] = political_1.doSeatsBias(s, bLog);
|
|
101251
|
-
s.tests[12 /* VotesBias */] = political_1.doVotesBias(s, bLog);
|
|
101252
|
-
s.tests[13 /* Responsiveness */] = political_1.doResponsiveness(s, bLog);
|
|
101253
|
-
s.tests[14 /* ResponsiveDistricts */] = political_1.doResponsiveDistricts(s, bLog);
|
|
101254
|
-
s.tests[15 /* EfficiencyGap */] = political_1.doEfficiencyGap(s, bLog);
|
|
101255
|
-
s.tests[16 /* MajorityMinorityDistricts */] = minority_1.doMajorityMinorityDistricts(s, bLog);
|
|
101256
|
-
}
|
|
101257
|
-
// Tests in the "Best" suite, i.e., criteria for better/worse
|
|
101258
|
-
if (requestedSuites.includes(2 /* Best */)) {
|
|
101259
|
-
s.tests[5 /* Reock */] = compact_1.doReock(s, bLog);
|
|
101260
|
-
s.tests[6 /* PolsbyPopper */] = compact_1.doPolsbyPopper(s, bLog);
|
|
101261
101727
|
s.tests[7 /* UnexpectedCountySplits */] = cohesive_1.doFindCountiesSplitUnexpectedly(s, bLog);
|
|
101262
101728
|
s.tests[10 /* VTDSplits */] = cohesive_1.doFindSplitVTDs(s, bLog);
|
|
101263
|
-
s.tests[8 /* CountySplitting */] = cohesive_1.doCountySplitting(s, bLog);
|
|
101264
|
-
s.tests[9 /* DistrictSplitting */] = cohesive_1.doDistrictSplitting(s, bLog);
|
|
101265
101729
|
}
|
|
101266
101730
|
// Enable a Test Log and Scorecard to be generated
|
|
101267
101731
|
s.bPlanAnalyzed = true;
|
|
@@ -101275,8 +101739,6 @@ exports.doAnalyzePlan = doAnalyzePlan;
|
|
|
101275
101739
|
// NOTE - Should this be conditionalized on the test suites requested?
|
|
101276
101740
|
// Those are encapsulated in reports.ts right now, so not doing that.
|
|
101277
101741
|
function doDeriveSecondaryTests(s, bLog = false) {
|
|
101278
|
-
// TODO - SCORE: Toggle
|
|
101279
|
-
// const bLegacy = s._bLegacy;
|
|
101280
101742
|
s.tests[3 /* EqualPopulation */] = equal_1.doHasEqualPopulations(s, bLog);
|
|
101281
101743
|
}
|
|
101282
101744
|
exports.doDeriveSecondaryTests = doDeriveSecondaryTests;
|
|
@@ -101896,18 +102358,11 @@ exports.doPopulationDeviation = doPopulationDeviation;
|
|
|
101896
102358
|
// NOTE - This validity check is *derived* and depends on population deviation %
|
|
101897
102359
|
// being computed (above) and normalized in test log & scorecard generation.
|
|
101898
102360
|
function doHasEqualPopulations(s, bLog = false) {
|
|
101899
|
-
// TODO - SCORE: Toggle
|
|
101900
|
-
// const bLegacy = s._bLegacy;
|
|
101901
|
-
const scorecard = s._scorecard;
|
|
101902
|
-
const popDevNormalizedNEW = scorecard.best.populationDeviation.normalized;
|
|
101903
|
-
const bScore = (popDevNormalizedNEW > 0) ? true : false;
|
|
101904
|
-
// TODO - Get scales
|
|
101905
|
-
// TODO - Flow through to RequirementsChecklist
|
|
101906
102361
|
let test = s.getTest(3 /* EqualPopulation */);
|
|
101907
102362
|
// Get the normalized population deviation %
|
|
101908
102363
|
let popDevTest = s.getTest(4 /* PopulationDeviation */);
|
|
101909
|
-
|
|
101910
|
-
|
|
102364
|
+
const popDevPct = popDevTest['score'];
|
|
102365
|
+
const popDevNormalized = popDevTest['normalizedScore'];
|
|
101911
102366
|
// Populate the test entry
|
|
101912
102367
|
if (popDevNormalized > 0) {
|
|
101913
102368
|
test['score'] = true;
|
|
@@ -101917,7 +102372,6 @@ function doHasEqualPopulations(s, bLog = false) {
|
|
|
101917
102372
|
}
|
|
101918
102373
|
test['details']['deviation'] = popDevPct;
|
|
101919
102374
|
test['details']['thresholds'] = popDevTest['details']['scale'];
|
|
101920
|
-
console.log("Equal population =", test['score'], bScore);
|
|
101921
102375
|
// Populate the N+1 summary "district" in district.statistics
|
|
101922
102376
|
let bEqualPop = s.districts.statistics[D.DistrictField.bEqualPop];
|
|
101923
102377
|
let summaryRow = s.districts.numberOfRows() - 1;
|
|
@@ -102512,12 +102966,14 @@ function prepareRequirementsChecklist(s, bLog = false) {
|
|
|
102512
102966
|
const emptyDistrictsDetail = U.deepCopy(completeTest['details']['emptyDistricts']) || [];
|
|
102513
102967
|
const discontiguousDistrictsDetail = U.deepCopy(contiguousTest['details']['discontiguousDistricts']) || [];
|
|
102514
102968
|
const embeddedDistrictsDetail = U.deepCopy(freeOfHolesTest['details']['embeddedDistricts']) || [];
|
|
102515
|
-
|
|
102516
|
-
const
|
|
102517
|
-
//
|
|
102969
|
+
// TODO - SCORE: DELETE - This code is hooked correctly to use dra-score
|
|
102970
|
+
// const scorecard = s._scorecard as Score.Scorecard;
|
|
102971
|
+
// const populationDeviation = scorecard.best.populationDeviation.raw;
|
|
102518
102972
|
const populationDeviationDetail = U.deepCopy(equalPopulationTest['details']['deviation']);
|
|
102973
|
+
// console.log("Population deviations =", populationDeviationDetail, populationDeviation);
|
|
102974
|
+
// const deviationThreshold = scorecard.best.populationDeviation.notes['threshold'];
|
|
102519
102975
|
const deviationThresholdDetail = U.trim(s.populationDeviationThreshold());
|
|
102520
|
-
console.log("Population deviation thresholds =", deviationThresholdDetail, deviationThreshold);
|
|
102976
|
+
// console.log("Population deviation thresholds =", deviationThresholdDetail, deviationThreshold);
|
|
102521
102977
|
const xx = s.state.xx;
|
|
102522
102978
|
// TODO - JSON: Is there a better / easier way to work with the variable?
|
|
102523
102979
|
const stateReqsDict = state_reqs_json_1.default;
|
|
@@ -102785,20 +103241,29 @@ exports.doConfigureScales = doConfigureScales;
|
|
|
102785
103241
|
// Do this after analytics have been run and before preparing a test log or scorecard.
|
|
102786
103242
|
function doAnalyzePostProcessing(s, bLog = false) {
|
|
102787
103243
|
// TODO - SCORE: Toggle
|
|
102788
|
-
|
|
102789
|
-
|
|
102790
|
-
|
|
102791
|
-
|
|
102792
|
-
|
|
102793
|
-
|
|
102794
|
-
|
|
102795
|
-
|
|
102796
|
-
|
|
102797
|
-
|
|
102798
|
-
|
|
102799
|
-
|
|
103244
|
+
if (s.useLegacy()) {
|
|
103245
|
+
// Normalize the raw scores for all the numerics tests
|
|
103246
|
+
let testResults = U.getNumericObjectKeys(testDefns);
|
|
103247
|
+
for (let testID of testResults) {
|
|
103248
|
+
if (testDefns[testID]['normalize']) {
|
|
103249
|
+
let testResult = s.getTest(testID);
|
|
103250
|
+
let rawScore = testResult['score'];
|
|
103251
|
+
let normalizedScore;
|
|
103252
|
+
normalizedScore = U.normalize(rawScore, s.testScales[testID]);
|
|
103253
|
+
testResult['normalizedScore'] = normalizedScore;
|
|
103254
|
+
// Add the scale used to normalize the raw score to the details
|
|
103255
|
+
testResult['details']['scale'] = s.testScales[testID].scale;
|
|
103256
|
+
}
|
|
102800
103257
|
}
|
|
102801
103258
|
}
|
|
103259
|
+
else {
|
|
103260
|
+
// Just populate the normalized population deviation score in the test
|
|
103261
|
+
const scorecard = s._scorecard;
|
|
103262
|
+
let test = s.getTest(4 /* PopulationDeviation */);
|
|
103263
|
+
test['normalizedScore'] = scorecard.traditionalPrinciples.populationDeviation.normalized;
|
|
103264
|
+
// TODO - DELETE
|
|
103265
|
+
// test['normalizedScore'] = scorecard.best.populationDeviation.normalized;
|
|
103266
|
+
}
|
|
102802
103267
|
// Derive secondary tests
|
|
102803
103268
|
analyze_1.doDeriveSecondaryTests(s, bLog);
|
|
102804
103269
|
// Toggle the semaphore, so postprocessing isn't for both the testlog & scorecard
|
|
@@ -102829,7 +103294,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
102829
103294
|
return result;
|
|
102830
103295
|
};
|
|
102831
103296
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
102832
|
-
const Score = __importStar(__webpack_require__(/*! @dra2020/dra-score */ "
|
|
103297
|
+
const Score = __importStar(__webpack_require__(/*! @dra2020/dra-score */ "./node_modules/@dra2020/dra-score/dist/dra-score.bundle.js"));
|
|
102833
103298
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
102834
103299
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
102835
103300
|
// PROFILE A PLAN
|
|
@@ -102843,9 +103308,10 @@ function profilePlan(s, bLog = false) {
|
|
|
102843
103308
|
const geoPropsByDistrict = makeArrayOfGeoProps(s, bLog);
|
|
102844
103309
|
const splits = makeNakedCxD(s);
|
|
102845
103310
|
const summaryRow = s.districts.numberOfRows() - 1;
|
|
102846
|
-
const statewideVf = s.districts.statistics[D.DistrictField.DemPct][summaryRow];
|
|
103311
|
+
const statewideVf = U.trim(s.districts.statistics[D.DistrictField.DemPct][summaryRow], 6);
|
|
102847
103312
|
const vpiArray = U.deepCopy(s.districts.statistics[D.DistrictField.DemPct].slice(1, -1));
|
|
102848
|
-
const
|
|
103313
|
+
const statewideDemographics = getStatewideDemographics(s);
|
|
103314
|
+
const demographicsByDistrict = getDemographicsByDistrict(s);
|
|
102849
103315
|
const profile = {
|
|
102850
103316
|
state: state,
|
|
102851
103317
|
planName: planName,
|
|
@@ -102853,21 +103319,22 @@ function profilePlan(s, bLog = false) {
|
|
|
102853
103319
|
nCounties: nCounties,
|
|
102854
103320
|
legislativeDistricts: s.legislativeDistricts,
|
|
102855
103321
|
populationProfile: {
|
|
102856
|
-
|
|
103322
|
+
totalPopByDistrict: popByDistrict,
|
|
102857
103323
|
targetSize: targetSize
|
|
102858
103324
|
},
|
|
102859
103325
|
compactnessProfile: {
|
|
102860
|
-
|
|
103326
|
+
geometryByDistrict: geoPropsByDistrict
|
|
102861
103327
|
},
|
|
102862
103328
|
splittingProfile: {
|
|
102863
|
-
|
|
103329
|
+
countyPopByDistrict: splits
|
|
102864
103330
|
},
|
|
102865
103331
|
partisanProfile: {
|
|
102866
103332
|
statewideVf: statewideVf,
|
|
102867
|
-
|
|
103333
|
+
vfArray: vpiArray
|
|
102868
103334
|
},
|
|
102869
103335
|
demographicProfile: {
|
|
102870
|
-
|
|
103336
|
+
stateMfArray: statewideDemographics,
|
|
103337
|
+
mfArrayByDistrict: demographicsByDistrict
|
|
102871
103338
|
}
|
|
102872
103339
|
};
|
|
102873
103340
|
return profile;
|
|
@@ -102901,7 +103368,7 @@ function makeArrayOfGeoProps(s, bLog = false) {
|
|
|
102901
103368
|
}
|
|
102902
103369
|
return geometryByDistrict;
|
|
102903
103370
|
}
|
|
102904
|
-
function
|
|
103371
|
+
function getDemographicsByDistrict(s, bLog = false) {
|
|
102905
103372
|
let demographicsArray = [];
|
|
102906
103373
|
// Remove the unassigned & total dummy "districts"
|
|
102907
103374
|
for (let districtID = 1; districtID <= s.state.nDistricts; districtID++) {
|
|
@@ -102918,10 +103385,44 @@ function makeArrayOfDemographics(s, bLog = false) {
|
|
|
102918
103385
|
}
|
|
102919
103386
|
return demographicsArray;
|
|
102920
103387
|
}
|
|
103388
|
+
function getStatewideDemographics(s, bLog = false) {
|
|
103389
|
+
const summaryRow = s.districts.numberOfRows() - 1;
|
|
103390
|
+
const demographicsArray = [
|
|
103391
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.WhitePct][summaryRow]),
|
|
103392
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.MinorityPct][summaryRow]),
|
|
103393
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.BlackPct][summaryRow]),
|
|
103394
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.HispanicPct][summaryRow]),
|
|
103395
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.PacificPct][summaryRow]),
|
|
103396
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.AsianPct][summaryRow]),
|
|
103397
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.NativePct][summaryRow])
|
|
103398
|
+
];
|
|
103399
|
+
return demographicsArray;
|
|
103400
|
+
}
|
|
102921
103401
|
// SCORE A PLAN
|
|
102922
|
-
function scorePlan(p, bLog = false) {
|
|
103402
|
+
function scorePlan(s, p, bLog = false, overridesJSON) {
|
|
102923
103403
|
let scorer = new Score.Scorer();
|
|
102924
|
-
|
|
103404
|
+
const scorecard = scorer.score(p, overridesJSON);
|
|
103405
|
+
// TODO - SCORE: Toggle: Before returning, create a dummy population deviation
|
|
103406
|
+
// test, for doHasEqualPopulations() to use later. This is preserving the old
|
|
103407
|
+
// calling sequence.
|
|
103408
|
+
let test = s.getTest(4 /* PopulationDeviation */);
|
|
103409
|
+
// TODO - SCORE: U.trim(popDev)???
|
|
103410
|
+
// const popDev = scorecard.best.populationDeviation.raw;
|
|
103411
|
+
// Get the raw population deviation
|
|
103412
|
+
const popDev = scorecard.traditionalPrinciples.populationDeviation.raw;
|
|
103413
|
+
// Populate the test entry
|
|
103414
|
+
test['score'] = popDev;
|
|
103415
|
+
test['details'] = { 'maxDeviation': scorecard.traditionalPrinciples.populationDeviation.notes['maxDeviation'] };
|
|
103416
|
+
// TODO - DELETE
|
|
103417
|
+
// test['details'] = { 'maxDeviation': scorecard.best.populationDeviation.notes['maxDeviation'] };
|
|
103418
|
+
// Populate the N+1 summary "district" in district.statistics
|
|
103419
|
+
let totalPop = s.districts.statistics[D.DistrictField.TotalPop];
|
|
103420
|
+
let popDevPct = s.districts.statistics[D.DistrictField.PopDevPct];
|
|
103421
|
+
let summaryRow = s.districts.numberOfRows() - 1;
|
|
103422
|
+
totalPop[summaryRow] = p.populationProfile.targetSize;
|
|
103423
|
+
popDevPct[summaryRow] = popDev;
|
|
103424
|
+
//
|
|
103425
|
+
return scorecard;
|
|
102925
103426
|
}
|
|
102926
103427
|
exports.scorePlan = scorePlan;
|
|
102927
103428
|
|