@dra2020/district-analytics 3.2.0 → 3.3.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 +611 -328
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +15 -5
- package/dist/district-analytics.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -89264,7 +89264,15 @@ 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() {
|
|
@@ -89272,6 +89280,9 @@ class Scorer {
|
|
|
89272
89280
|
score(profile, overridesJSON) {
|
|
89273
89281
|
return score_1.scorePlan(profile, overridesJSON);
|
|
89274
89282
|
}
|
|
89283
|
+
populationDeviationThreshold(bLegislative) {
|
|
89284
|
+
return C.popdevThreshold(bLegislative);
|
|
89285
|
+
}
|
|
89275
89286
|
}
|
|
89276
89287
|
exports.Scorer = Scorer;
|
|
89277
89288
|
|
|
@@ -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,202 @@ 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, 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]},\"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
|
+
// TRADITIONAL DISTRICTING PRINCIPLES
|
|
89887
|
+
function compactnessWeight(context, overridesJSON) {
|
|
89888
|
+
const cW = config_json_1.default.traditionalPrinciples.compactness.weight[context];
|
|
89889
|
+
return cW;
|
|
89890
|
+
}
|
|
89891
|
+
exports.compactnessWeight = compactnessWeight;
|
|
89892
|
+
function reockWeight(overridesJSON) {
|
|
89893
|
+
const rW = config_json_1.default.traditionalPrinciples.compactness.reock.weight;
|
|
89894
|
+
return rW;
|
|
89895
|
+
}
|
|
89896
|
+
exports.reockWeight = reockWeight;
|
|
89897
|
+
function reockRange(overridesJSON) {
|
|
89898
|
+
const range = config_json_1.default.traditionalPrinciples.compactness.reock.range;
|
|
89899
|
+
return range;
|
|
89900
|
+
}
|
|
89901
|
+
exports.reockRange = reockRange;
|
|
89902
|
+
function polsbyWeight(overridesJSON) {
|
|
89903
|
+
const ppW = config_json_1.default.traditionalPrinciples.compactness.polsby.weight;
|
|
89904
|
+
return ppW;
|
|
89905
|
+
}
|
|
89906
|
+
exports.polsbyWeight = polsbyWeight;
|
|
89907
|
+
function polsbyRange(overridesJSON) {
|
|
89908
|
+
const range = config_json_1.default.traditionalPrinciples.compactness.polsby.range;
|
|
89909
|
+
return range;
|
|
89910
|
+
}
|
|
89911
|
+
exports.polsbyRange = polsbyRange;
|
|
89912
|
+
function splittingWeight(context, overridesJSON) {
|
|
89913
|
+
const sW = config_json_1.default.traditionalPrinciples.splitting.weight[context];
|
|
89914
|
+
return sW;
|
|
89915
|
+
}
|
|
89916
|
+
exports.splittingWeight = splittingWeight;
|
|
89917
|
+
function countySplittingWeight(overridesJSON) {
|
|
89918
|
+
const csW = config_json_1.default.traditionalPrinciples.splitting.county.weight;
|
|
89919
|
+
return csW;
|
|
89920
|
+
}
|
|
89921
|
+
exports.countySplittingWeight = countySplittingWeight;
|
|
89922
|
+
function countySplittingRange(overridesJSON) {
|
|
89923
|
+
const range = config_json_1.default.traditionalPrinciples.splitting.county.range;
|
|
89924
|
+
return range;
|
|
89925
|
+
}
|
|
89926
|
+
exports.countySplittingRange = countySplittingRange;
|
|
89927
|
+
function allowableSplitsMultiplier(overridesJSON) {
|
|
89928
|
+
const m = config_json_1.default.traditionalPrinciples.splitting.county.allowableSplitsMultiplier;
|
|
89929
|
+
return m;
|
|
89930
|
+
}
|
|
89931
|
+
exports.allowableSplitsMultiplier = allowableSplitsMultiplier;
|
|
89932
|
+
function districtSplittingWeight(overridesJSON) {
|
|
89933
|
+
const dsW = config_json_1.default.traditionalPrinciples.splitting.district.weight;
|
|
89934
|
+
return dsW;
|
|
89935
|
+
}
|
|
89936
|
+
exports.districtSplittingWeight = districtSplittingWeight;
|
|
89937
|
+
function districtSplittingRange(overridesJSON) {
|
|
89938
|
+
const range = config_json_1.default.traditionalPrinciples.splitting.district.range;
|
|
89939
|
+
return range;
|
|
89940
|
+
}
|
|
89941
|
+
exports.districtSplittingRange = districtSplittingRange;
|
|
89942
|
+
function popdevWeight(context, overridesJSON) {
|
|
89943
|
+
const pdW = config_json_1.default.traditionalPrinciples.popdev.weight[context];
|
|
89944
|
+
return pdW;
|
|
89945
|
+
}
|
|
89946
|
+
exports.popdevWeight = popdevWeight;
|
|
89947
|
+
// NOTE - Raw ranges, not inverted (i.e., smaller is better)
|
|
89948
|
+
// NOTE - This could be optimized to not calc LD values for CD's (or do it once)
|
|
89949
|
+
function popdevRange(bLegislative, overridesJSON) {
|
|
89950
|
+
const cdRange = config_json_1.default.traditionalPrinciples.popdev.range[0 /* Congressional */];
|
|
89951
|
+
const worstCD = cdRange[exports.BEG];
|
|
89952
|
+
const bestCD = cdRange[exports.END];
|
|
89953
|
+
const ldRange = config_json_1.default.traditionalPrinciples.popdev.range[1 /* StateLegislative */];
|
|
89954
|
+
const iRange = bLegislative ? ldRange : cdRange;
|
|
89955
|
+
const worst = iRange[exports.BEG];
|
|
89956
|
+
const best = bLegislative ? (bestCD / worstCD) * iRange[exports.BEG] : iRange[exports.END];
|
|
89957
|
+
// Invert the range, so bigger is better.
|
|
89958
|
+
return [worst, best];
|
|
89959
|
+
}
|
|
89960
|
+
exports.popdevRange = popdevRange;
|
|
89961
|
+
// NOTE - Raw threshold, not inverted (i.e., smaller is better)
|
|
89962
|
+
function popdevThreshold(bLegislative, overridesJSON) {
|
|
89963
|
+
const threshold = popdevRange(bLegislative)[exports.BEG];
|
|
89964
|
+
return threshold;
|
|
89965
|
+
}
|
|
89966
|
+
exports.popdevThreshold = popdevThreshold;
|
|
89967
|
+
// OVERALL SCORE
|
|
89968
|
+
function partisanWeight(context, overridesJSON) {
|
|
89969
|
+
const pW = config_json_1.default.partisan.weight[context];
|
|
89970
|
+
return pW;
|
|
89971
|
+
}
|
|
89972
|
+
exports.partisanWeight = partisanWeight;
|
|
89973
|
+
function traditionalPrinciplesWeight(context, overridesJSON) {
|
|
89974
|
+
const tpW = config_json_1.default.traditionalPrinciples.weight[context];
|
|
89975
|
+
return tpW;
|
|
89976
|
+
}
|
|
89977
|
+
exports.traditionalPrinciplesWeight = traditionalPrinciplesWeight;
|
|
89978
|
+
|
|
89979
|
+
|
|
89773
89980
|
/***/ }),
|
|
89774
89981
|
|
|
89775
89982
|
/***/ "./src/equal.ts":
|
|
@@ -89793,7 +90000,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
89793
90000
|
};
|
|
89794
90001
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
89795
90002
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
89796
|
-
const
|
|
90003
|
+
const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
89797
90004
|
const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
|
|
89798
90005
|
// Migrated from district-analytics
|
|
89799
90006
|
// Expect a full dict of district IDs & values (which might be zero = empty)
|
|
@@ -89814,7 +90021,7 @@ function doPopulationDeviation(e, targetSize, bLegislative, bLog = false) {
|
|
|
89814
90021
|
// Round the raw value to the desired level of precision
|
|
89815
90022
|
const popDev = U.trim((max - min) / targetSize);
|
|
89816
90023
|
const score = scorePopulationDeviation(popDev, bLegislative);
|
|
89817
|
-
const threshold = bLegislative
|
|
90024
|
+
const threshold = C.popdevThreshold(bLegislative);
|
|
89818
90025
|
const notes = {
|
|
89819
90026
|
'maxDeviation': max - min,
|
|
89820
90027
|
'threshold': threshold
|
|
@@ -89830,11 +90037,11 @@ function doPopulationDeviation(e, targetSize, bLegislative, bLog = false) {
|
|
|
89830
90037
|
exports.doPopulationDeviation = doPopulationDeviation;
|
|
89831
90038
|
function scorePopulationDeviation(rawValue, bLegislative) {
|
|
89832
90039
|
const _normalizer = new normalize_1.Normalizer(rawValue);
|
|
89833
|
-
|
|
89834
|
-
const
|
|
90040
|
+
// Raw range in not inverted (i.e., smaller is better)
|
|
90041
|
+
const range = C.popdevRange(bLegislative);
|
|
89835
90042
|
_normalizer.invert();
|
|
89836
|
-
_normalizer.clip(
|
|
89837
|
-
_normalizer.unitize(
|
|
90043
|
+
_normalizer.clip(1.0 - range[C.BEG], 1.0 - range[C.END]);
|
|
90044
|
+
_normalizer.unitize(1.0 - range[C.BEG], 1.0 - range[C.END]);
|
|
89838
90045
|
_normalizer.rescale();
|
|
89839
90046
|
return _normalizer.normalizedNum;
|
|
89840
90047
|
}
|
|
@@ -89843,10 +90050,129 @@ exports.scorePopulationDeviation = scorePopulationDeviation;
|
|
|
89843
90050
|
|
|
89844
90051
|
/***/ }),
|
|
89845
90052
|
|
|
89846
|
-
/***/ "./src/
|
|
89847
|
-
|
|
89848
|
-
!*** ./src/
|
|
89849
|
-
|
|
90053
|
+
/***/ "./src/index.ts":
|
|
90054
|
+
/*!**********************!*\
|
|
90055
|
+
!*** ./src/index.ts ***!
|
|
90056
|
+
\**********************/
|
|
90057
|
+
/*! no static exports found */
|
|
90058
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
90059
|
+
|
|
90060
|
+
"use strict";
|
|
90061
|
+
|
|
90062
|
+
//
|
|
90063
|
+
// SCORE PACKAGE API
|
|
90064
|
+
//
|
|
90065
|
+
function __export(m) {
|
|
90066
|
+
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
|
90067
|
+
}
|
|
90068
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90069
|
+
__export(__webpack_require__(/*! ./_api */ "./src/_api.ts"));
|
|
90070
|
+
var types_1 = __webpack_require__(/*! ./types */ "./src/types.ts");
|
|
90071
|
+
exports.sampleProfile = types_1.sampleProfile;
|
|
90072
|
+
exports.sampleScorecard = types_1.sampleScorecard;
|
|
90073
|
+
|
|
90074
|
+
|
|
90075
|
+
/***/ }),
|
|
90076
|
+
|
|
90077
|
+
/***/ "./src/normalize.ts":
|
|
90078
|
+
/*!**************************!*\
|
|
90079
|
+
!*** ./src/normalize.ts ***!
|
|
90080
|
+
\**************************/
|
|
90081
|
+
/*! no static exports found */
|
|
90082
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
90083
|
+
|
|
90084
|
+
"use strict";
|
|
90085
|
+
|
|
90086
|
+
//
|
|
90087
|
+
// NORMALIZATION UTILITIES
|
|
90088
|
+
//
|
|
90089
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
90090
|
+
if (mod && mod.__esModule) return mod;
|
|
90091
|
+
var result = {};
|
|
90092
|
+
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
90093
|
+
result["default"] = mod;
|
|
90094
|
+
return result;
|
|
90095
|
+
};
|
|
90096
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90097
|
+
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
90098
|
+
class Normalizer {
|
|
90099
|
+
constructor(rawValue) {
|
|
90100
|
+
this.rawNum = rawValue;
|
|
90101
|
+
this.wipNum = this.rawNum;
|
|
90102
|
+
}
|
|
90103
|
+
// *Don't* transform the input.
|
|
90104
|
+
identity() {
|
|
90105
|
+
return this.wipNum;
|
|
90106
|
+
}
|
|
90107
|
+
positive() {
|
|
90108
|
+
this.wipNum = Math.abs(this.wipNum);
|
|
90109
|
+
return this.wipNum;
|
|
90110
|
+
}
|
|
90111
|
+
// Invert a value in the unit range [0.0–1.0] (so that bigger is better).
|
|
90112
|
+
invert() {
|
|
90113
|
+
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
90114
|
+
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Inverting: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
90115
|
+
this.wipNum = 1.0 - this.wipNum;
|
|
90116
|
+
return this.wipNum;
|
|
90117
|
+
}
|
|
90118
|
+
// Constrain the value to be within a specified range.
|
|
90119
|
+
clip(begin_range, end_range) {
|
|
90120
|
+
// Handle the ends of the range being given either order
|
|
90121
|
+
const min_range = Math.min(begin_range, end_range);
|
|
90122
|
+
const max_range = Math.max(begin_range, end_range);
|
|
90123
|
+
this.wipNum = Math.max(Math.min(this.wipNum, max_range), min_range);
|
|
90124
|
+
return this.wipNum;
|
|
90125
|
+
}
|
|
90126
|
+
// Recast the value as the delta from a baseline value. NOOP if it is zero.
|
|
90127
|
+
// NOTE - Values can be + or -.
|
|
90128
|
+
rebase(base) {
|
|
90129
|
+
this.wipNum = this.wipNum - base;
|
|
90130
|
+
return this.wipNum;
|
|
90131
|
+
}
|
|
90132
|
+
// Re-scale a value into the [0.0 – 1.0] range, using a given range.
|
|
90133
|
+
// NOTE - This assumes that values have alrady been clipped into the range.
|
|
90134
|
+
unitize(begin_range, end_range) {
|
|
90135
|
+
const min_range = Math.min(begin_range, end_range);
|
|
90136
|
+
const max_range = Math.max(begin_range, end_range);
|
|
90137
|
+
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
90138
|
+
console.assert(((this.wipNum >= min_range) && (this.wipNum <= max_range)), "Unitizing: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
90139
|
+
const ranged = this.wipNum - min_range;
|
|
90140
|
+
this.wipNum = Math.abs(ranged / (end_range - begin_range));
|
|
90141
|
+
return this.wipNum;
|
|
90142
|
+
}
|
|
90143
|
+
// Decay a value in the unit range [0.0–1.0] by its distance from zero.
|
|
90144
|
+
// NOTE - If the range is already such that "bigger is better," then the closer
|
|
90145
|
+
// the value is to 1.0 (the best) the *less* it will decay.
|
|
90146
|
+
decay(fn = 0 /* Gravity */) {
|
|
90147
|
+
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
90148
|
+
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Decaying: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
90149
|
+
switch (fn) {
|
|
90150
|
+
case 0 /* Gravity */: {
|
|
90151
|
+
this.wipNum = Math.pow(this.wipNum, S.DISTANCE_WEIGHT);
|
|
90152
|
+
return this.wipNum;
|
|
90153
|
+
}
|
|
90154
|
+
default: {
|
|
90155
|
+
throw new Error("Decay function not recognized.");
|
|
90156
|
+
}
|
|
90157
|
+
}
|
|
90158
|
+
}
|
|
90159
|
+
// Translate a value in the unit range to the user-friendly range [0 – 100].
|
|
90160
|
+
rescale() {
|
|
90161
|
+
// TODO - DAVID: Can we make this throw an Error and expect that in unit test?
|
|
90162
|
+
console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Rescaling: " + S.OUT_OF_RANGE_MSG, this.wipNum);
|
|
90163
|
+
this.normalizedNum = Math.round(this.wipNum * S.NORMALIZED_RANGE);
|
|
90164
|
+
return this.normalizedNum;
|
|
90165
|
+
}
|
|
90166
|
+
}
|
|
90167
|
+
exports.Normalizer = Normalizer;
|
|
90168
|
+
|
|
90169
|
+
|
|
90170
|
+
/***/ }),
|
|
90171
|
+
|
|
90172
|
+
/***/ "./src/partisan.ts":
|
|
90173
|
+
/*!*************************!*\
|
|
90174
|
+
!*** ./src/partisan.ts ***!
|
|
90175
|
+
\*************************/
|
|
89850
90176
|
/*! no static exports found */
|
|
89851
90177
|
/***/ (function(module, exports, __webpack_require__) {
|
|
89852
90178
|
|
|
@@ -89865,48 +90191,61 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
89865
90191
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
89866
90192
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
89867
90193
|
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
90194
|
+
const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
89868
90195
|
const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
|
|
89869
90196
|
// NOTE - I'm passing T.VfArray's into everything. District indices = array indices.
|
|
89870
90197
|
// NOTE - I do not (cannot) assume that the values are sorted.
|
|
89871
90198
|
// SCORE BIAS & COMPETITIVENESS
|
|
89872
|
-
/* SCORECARD FIELDS:
|
|
90199
|
+
/* SCORECARD FIELDS <<< TODO: Update
|
|
89873
90200
|
|
|
89874
90201
|
* ^S# [BestS] = the Democratic seats closest to proportional
|
|
89875
90202
|
* ^S% [BestSf] = the corresponding Democratic seat share
|
|
89876
|
-
*
|
|
90203
|
+
* S! [FptpS] = the estimated number of Democratic seats using first past the post
|
|
89877
90204
|
* S# [ProbableS] = the estimated Democratic seats, using seat probabilities
|
|
89878
90205
|
* S% [ProbableSf] = the estimated Democratic seat share fraction, calculated as S# / N
|
|
90206
|
+
* UE# [UnearnedS] = the number of unearned seats (R = positive; D = negative)
|
|
89879
90207
|
* B% [Bias]= the bias calculated as S% – ^S%
|
|
89880
90208
|
* B$ [BiasScore] = the bias score normalized [0–100]
|
|
89881
90209
|
|
|
89882
90210
|
* R# [R] = the responsiveness at V%
|
|
89883
90211
|
* Rd [Rd] = the estimated # of responsive districts (using probabilities)
|
|
89884
90212
|
* Rd% [Rdf] = the estimated # of responsive districts, as a fraction of N
|
|
90213
|
+
|
|
90214
|
+
* C [C] = the number of districts that fall into the range [45–55%]
|
|
89885
90215
|
* Cd [Cd] = the estimated # of competitive districts, using probabilities & a narrower [0.25–0.75] range
|
|
89886
90216
|
* Cd% [Cdf] = the estimated # of competitive districts, as a fraction of N
|
|
90217
|
+
* [beg, end] [Mrange] = the 1–N indices for the first and last district that separate the likely result and the proportional result
|
|
90218
|
+
* Md [Md] = the competitiveness of the marginal districts
|
|
89887
90219
|
* Md% [Mdf] = the probability that the marginal seats will flip, so S% => ^S%
|
|
89888
90220
|
* C$ [CompetitiveScore] = the competitiveness score normalized [0–100]
|
|
89889
90221
|
|
|
89890
|
-
*
|
|
90222
|
+
* F$ = the combined partisan score normalize [0–100]
|
|
89891
90223
|
|
|
89892
90224
|
*/
|
|
89893
|
-
function scorePartisan(Vf, VfArray,
|
|
90225
|
+
function scorePartisan(Vf, VfArray, bAll = false, bConstrained = true) {
|
|
89894
90226
|
const N = VfArray.length;
|
|
89895
90227
|
const BestS = bestSeats(N, Vf);
|
|
89896
90228
|
const BestSf = bestSeatShare(BestS, N);
|
|
89897
|
-
const FptpS =
|
|
89898
|
-
const
|
|
90229
|
+
const FptpS = bAll ? estFPTPSeats(VfArray) : undefined;
|
|
90230
|
+
const range = bConstrained ? C.competitiveDistribution() : undefined;
|
|
90231
|
+
const ProbableS = estProbableSeats(VfArray, range);
|
|
89899
90232
|
const ProbableSf = estProbableSeatShare(ProbableS, N);
|
|
89900
90233
|
const Bias = estBias(ProbableSf, BestSf);
|
|
89901
|
-
const
|
|
89902
|
-
const
|
|
89903
|
-
const
|
|
89904
|
-
const
|
|
89905
|
-
const
|
|
89906
|
-
const
|
|
90234
|
+
const biasScore = scoreBias(Bias, Vf, ProbableSf, N);
|
|
90235
|
+
const UnearnedS = estUnearnedSeats(BestS, ProbableS);
|
|
90236
|
+
const impactScore = scoreImpact(UnearnedS);
|
|
90237
|
+
const bInferSV = (bAll || (!bConstrained));
|
|
90238
|
+
const inferredSVpoints = bInferSV ? inferSVpoints(Vf, VfArray, range) : undefined;
|
|
90239
|
+
const R = bInferSV ? estResponsiveness(Vf, inferredSVpoints) : undefined;
|
|
90240
|
+
const Rd = bInferSV ? estResponsiveDistricts(VfArray) : undefined;
|
|
90241
|
+
const Rdf = bInferSV ? estResponsiveDistrictsShare(Rd, N) : undefined;
|
|
90242
|
+
const Cn = countCompetitiveDistricts(VfArray);
|
|
90243
|
+
const Cd = bConstrained ? estCompetitiveDistricts(VfArray) : Rd;
|
|
89907
90244
|
const Cdf = estCompetitiveDistrictsShare(Cd, N);
|
|
89908
|
-
const
|
|
89909
|
-
const
|
|
90245
|
+
const Mrange = findMarginalDistricts(Vf, VfArray, N);
|
|
90246
|
+
const Md = estMarginalCompetitiveDistricts(Mrange, VfArray);
|
|
90247
|
+
const Mdf = estMarginalCompetitiveShare(Md, Mrange);
|
|
90248
|
+
const competitivenessScore = scoreCompetitiveness(Mdf, Cdf);
|
|
89910
90249
|
const biasScoring = {
|
|
89911
90250
|
BestS: BestS,
|
|
89912
90251
|
BestSf: BestSf,
|
|
@@ -89914,61 +90253,73 @@ function scorePartisan(Vf, VfArray, all = false) {
|
|
|
89914
90253
|
ProbableS: ProbableS,
|
|
89915
90254
|
ProbableSf: ProbableSf,
|
|
89916
90255
|
Bias: Bias,
|
|
89917
|
-
|
|
90256
|
+
score: biasScore
|
|
89918
90257
|
};
|
|
89919
|
-
|
|
89920
|
-
|
|
89921
|
-
|
|
89922
|
-
|
|
89923
|
-
|
|
89924
|
-
|
|
89925
|
-
|
|
89926
|
-
|
|
89927
|
-
|
|
89928
|
-
|
|
89929
|
-
|
|
89930
|
-
|
|
89931
|
-
|
|
89932
|
-
|
|
89933
|
-
|
|
89934
|
-
|
|
89935
|
-
|
|
89936
|
-
|
|
89937
|
-
|
|
89938
|
-
|
|
90258
|
+
const impactScoring = {
|
|
90259
|
+
UnearnedS: UnearnedS,
|
|
90260
|
+
score: impactScore
|
|
90261
|
+
};
|
|
90262
|
+
let competitiveScoring = {
|
|
90263
|
+
R: R,
|
|
90264
|
+
Rd: Rd,
|
|
90265
|
+
Rdf: Rdf,
|
|
90266
|
+
C: Cn,
|
|
90267
|
+
Cd: Cd,
|
|
90268
|
+
Cdf: Cdf,
|
|
90269
|
+
Mrange: Mrange,
|
|
90270
|
+
Md: Md,
|
|
90271
|
+
Mdf: Mdf,
|
|
90272
|
+
score: competitivenessScore
|
|
90273
|
+
};
|
|
90274
|
+
if (bAll) {
|
|
90275
|
+
competitiveScoring.R = R;
|
|
90276
|
+
competitiveScoring.Rd = Rd;
|
|
90277
|
+
competitiveScoring.Rdf = Rdf;
|
|
90278
|
+
}
|
|
90279
|
+
// Weight bias, impact, and competitiveness into partisan scores to compare
|
|
90280
|
+
// plans across states and within a state.
|
|
90281
|
+
const bS = biasScoring.score;
|
|
90282
|
+
const iS = impactScoring.score;
|
|
90283
|
+
const cS = competitiveScoring.score;
|
|
90284
|
+
const acrossStatesPartisanScore = weightPartisan(bS, iS, cS, 0 /* AcrossStates */);
|
|
90285
|
+
const withinStatesPartisanScore = weightPartisan(bS, iS, cS, 1 /* WithinAState */);
|
|
89939
90286
|
const s = {
|
|
89940
90287
|
bias: biasScoring,
|
|
89941
|
-
|
|
90288
|
+
impact: impactScoring,
|
|
90289
|
+
competitiveness: competitiveScoring,
|
|
90290
|
+
score: acrossStatesPartisanScore,
|
|
90291
|
+
score2: withinStatesPartisanScore
|
|
89942
90292
|
};
|
|
89943
90293
|
return s;
|
|
89944
90294
|
}
|
|
89945
90295
|
exports.scorePartisan = scorePartisan;
|
|
89946
|
-
function weightPartisan(
|
|
89947
|
-
|
|
90296
|
+
function weightPartisan(bS, iS, cS, context) {
|
|
90297
|
+
const bW = C.biasWeight(context);
|
|
90298
|
+
const iW = C.impactWeight(context);
|
|
90299
|
+
const cW = C.competitivenessWeight(context);
|
|
90300
|
+
const score = Math.round(((bS * bW) + (iS * iW) + (cS * cW)) / (bW + iW + cW));
|
|
90301
|
+
return score;
|
|
89948
90302
|
}
|
|
89949
90303
|
exports.weightPartisan = weightPartisan;
|
|
89950
|
-
function
|
|
89951
|
-
console.log('XX, Name, N, V%, ^S#, ^S%,
|
|
90304
|
+
function printPartisanScorecardHeader() {
|
|
90305
|
+
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
90306
|
}
|
|
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.BestS, s.bias.BestSf, s.bias.FptpS, s.bias.ProbableS, s.bias.ProbableSf, s.bias.Bias, s.bias.
|
|
90307
|
+
exports.printPartisanScorecardHeader = printPartisanScorecardHeader;
|
|
90308
|
+
function printPartisanScorecardRow(xx, name, N, Vf, s) {
|
|
90309
|
+
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
90310
|
}
|
|
89957
|
-
exports.
|
|
90311
|
+
exports.printPartisanScorecardRow = printPartisanScorecardRow;
|
|
89958
90312
|
function scoreBias(rawBias, Vf, Sf, N) {
|
|
89959
90313
|
if (isAntimajoritarian(Vf, Sf)) {
|
|
89960
90314
|
return 0;
|
|
89961
90315
|
}
|
|
89962
90316
|
else {
|
|
89963
90317
|
const _normalizer = new normalize_1.Normalizer(rawBias);
|
|
89964
|
-
const
|
|
89965
|
-
const
|
|
89966
|
-
const best = S.BIAS_BEST;
|
|
90318
|
+
const worst = C.biasRange()[C.BEG];
|
|
90319
|
+
const best = C.biasRange()[C.END];
|
|
89967
90320
|
_normalizer.clip(worst, best);
|
|
89968
90321
|
_normalizer.unitize(worst, best);
|
|
89969
90322
|
_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
90323
|
// _normalizer.decay();
|
|
89973
90324
|
_normalizer.rescale();
|
|
89974
90325
|
const score = _normalizer.normalizedNum;
|
|
@@ -89976,6 +90327,20 @@ function scoreBias(rawBias, Vf, Sf, N) {
|
|
|
89976
90327
|
}
|
|
89977
90328
|
}
|
|
89978
90329
|
exports.scoreBias = scoreBias;
|
|
90330
|
+
// Normalize unearned seats
|
|
90331
|
+
function scoreImpact(rawUE) {
|
|
90332
|
+
const _normalizer = new normalize_1.Normalizer(rawUE);
|
|
90333
|
+
const worst = C.unearnedThreshold();
|
|
90334
|
+
const best = 0.0;
|
|
90335
|
+
_normalizer.positive();
|
|
90336
|
+
_normalizer.clip(worst, best);
|
|
90337
|
+
_normalizer.unitize(worst, best);
|
|
90338
|
+
_normalizer.invert();
|
|
90339
|
+
_normalizer.rescale();
|
|
90340
|
+
const score = _normalizer.normalizedNum;
|
|
90341
|
+
return score;
|
|
90342
|
+
}
|
|
90343
|
+
exports.scoreImpact = scoreImpact;
|
|
89979
90344
|
function isAntimajoritarian(Vf, Sf) {
|
|
89980
90345
|
const bDem = ((Vf < 0.5) && (Sf > 0.5)) ? true : false;
|
|
89981
90346
|
const bRep = (((1 - Vf) < 0.5) && ((1 - Sf) > 0.5)) ? true : false;
|
|
@@ -89987,16 +90352,24 @@ function scoreCompetitiveness(rawMarginal, rawOverall) {
|
|
|
89987
90352
|
// But the practical max is more like 2/3's, so unitize that range to [0.0–1.0].
|
|
89988
90353
|
// Then scale the values to [0–100].
|
|
89989
90354
|
const _overall = new normalize_1.Normalizer(rawOverall);
|
|
89990
|
-
|
|
89991
|
-
|
|
90355
|
+
let worst = C.overallCompetitivenessRange()[C.BEG];
|
|
90356
|
+
let best = C.overallCompetitivenessRange()[C.END];
|
|
90357
|
+
_overall.clip(worst, best);
|
|
90358
|
+
_overall.unitize(worst, best);
|
|
89992
90359
|
_overall.rescale();
|
|
89993
|
-
const
|
|
90360
|
+
const ocS = _overall.normalizedNum;
|
|
89994
90361
|
// Normalize marginal competitiveness
|
|
89995
90362
|
const _marginal = new normalize_1.Normalizer(rawMarginal);
|
|
90363
|
+
worst = C.marginalCompetitivenessRange()[C.BEG];
|
|
90364
|
+
best = C.marginalCompetitivenessRange()[C.END];
|
|
90365
|
+
_marginal.clip(worst, best);
|
|
90366
|
+
_marginal.unitize(worst, best);
|
|
89996
90367
|
_marginal.rescale();
|
|
89997
|
-
const
|
|
90368
|
+
const mcS = _marginal.normalizedNum;
|
|
90369
|
+
const mcW = C.marginalCompetitivenessWeight();
|
|
90370
|
+
const ocW = C.overallCompetitivenessWeight();
|
|
89998
90371
|
// Then combine the results
|
|
89999
|
-
const score = Math.round(((
|
|
90372
|
+
const score = Math.round(((mcW * mcS) + (ocW * ocS)) / (mcW + ocW));
|
|
90000
90373
|
return score;
|
|
90001
90374
|
}
|
|
90002
90375
|
exports.scoreCompetitiveness = scoreCompetitiveness;
|
|
@@ -90006,23 +90379,40 @@ const { erf } = __webpack_require__(/*! mathjs */ "./node_modules/mathjs/main/es
|
|
|
90006
90379
|
// console.log("erf(-0.5) =", erf(-0.5)); // returns -0.5204998778130465
|
|
90007
90380
|
// console.log("erf(4) =", erf(4)); // returns 0.9999999845827421
|
|
90008
90381
|
// Estimate the probability of a seat win for district, given a Vf
|
|
90009
|
-
function estSeatProbability(Vf) {
|
|
90382
|
+
function estSeatProbability(Vf, range) {
|
|
90383
|
+
if (range) {
|
|
90384
|
+
const _normalizer = new normalize_1.Normalizer(Vf);
|
|
90385
|
+
// The end points of a compressed probability distribution
|
|
90386
|
+
// NOTE - These aren't the points where races start or stop being contested,
|
|
90387
|
+
// just the end points of a distribution that yields the desired behavior
|
|
90388
|
+
// in the typical competitive range [45-55%].
|
|
90389
|
+
const distBeg = range[C.BEG];
|
|
90390
|
+
const distEnd = range[C.END];
|
|
90391
|
+
_normalizer.clip(distBeg, distEnd);
|
|
90392
|
+
_normalizer.unitize(distBeg, distEnd);
|
|
90393
|
+
return seatProbabilityFn(_normalizer.wipNum);
|
|
90394
|
+
}
|
|
90395
|
+
else {
|
|
90396
|
+
return seatProbabilityFn(Vf);
|
|
90397
|
+
}
|
|
90398
|
+
}
|
|
90399
|
+
exports.estSeatProbability = estSeatProbability;
|
|
90400
|
+
function seatProbabilityFn(Vf) {
|
|
90010
90401
|
// Python: 0.5 * (1 + erf((vpi - 0.50) / (0.02 * sqrt(8))))
|
|
90011
90402
|
return U.trim(0.5 * (1.0 + erf((Vf - 0.50) / (0.02 * Math.sqrt(8)))));
|
|
90012
90403
|
}
|
|
90013
|
-
exports.estSeatProbability = estSeatProbability;
|
|
90014
90404
|
// Estimate the number of responsive districts [R(d)], given a set of Vf's
|
|
90015
90405
|
function estDistrictResponsiveness(Vf) {
|
|
90016
90406
|
// Python: 1 - 4 * (est_seat_probability(vpi) - 0.5)**2
|
|
90017
90407
|
return U.trim(1.0 - 4.0 * Math.pow((estSeatProbability(Vf) - 0.5), 2));
|
|
90018
90408
|
}
|
|
90019
90409
|
exports.estDistrictResponsiveness = estDistrictResponsiveness;
|
|
90020
|
-
function inferSVpoints(Vf, VfArray) {
|
|
90410
|
+
function inferSVpoints(Vf, VfArray, range) {
|
|
90021
90411
|
const nDistricts = VfArray.length;
|
|
90022
90412
|
let SVpoints = [];
|
|
90023
90413
|
for (let shiftedVf of shiftRange()) {
|
|
90024
90414
|
const shiftedVPI = shiftDistricts(Vf, VfArray, shiftedVf);
|
|
90025
|
-
const shiftedSf = estProbableSeats(shiftedVPI) / nDistricts;
|
|
90415
|
+
const shiftedSf = estProbableSeats(shiftedVPI, range) / nDistricts;
|
|
90026
90416
|
SVpoints.push({ v: shiftedVf, s: shiftedSf });
|
|
90027
90417
|
}
|
|
90028
90418
|
return SVpoints;
|
|
@@ -90060,8 +90450,9 @@ function shiftRange() {
|
|
|
90060
90450
|
}
|
|
90061
90451
|
// ESTIMATE BIAS ("FAIR")
|
|
90062
90452
|
// ^S# - The # of Democratic seats closest to proportional @ statewide Vf
|
|
90453
|
+
// The "expected number of seats" from http://bit.ly/2Fcuf4q
|
|
90063
90454
|
function bestSeats(N, Vf) {
|
|
90064
|
-
return Math.round(N * Vf);
|
|
90455
|
+
return Math.round((N * Vf) - S.EPSILON);
|
|
90065
90456
|
}
|
|
90066
90457
|
exports.bestSeats = bestSeats;
|
|
90067
90458
|
// ^S% - The corresponding Democratic seat share
|
|
@@ -90070,9 +90461,9 @@ function bestSeatShare(bestS, N) {
|
|
|
90070
90461
|
}
|
|
90071
90462
|
exports.bestSeatShare = bestSeatShare;
|
|
90072
90463
|
// S# - The estimated # of Democratic seats @ statewide Vf, using seat probabilities
|
|
90073
|
-
function estProbableSeats(VfArray) {
|
|
90464
|
+
function estProbableSeats(VfArray, range) {
|
|
90074
90465
|
// Python: sum([est_seat_probability(vpi) for vpi in vpi_by_district])
|
|
90075
|
-
return U.trim(U.sumArray(VfArray.map(v => estSeatProbability(v))));
|
|
90466
|
+
return U.trim(U.sumArray(VfArray.map(v => estSeatProbability(v, range))));
|
|
90076
90467
|
}
|
|
90077
90468
|
exports.estProbableSeats = estProbableSeats;
|
|
90078
90469
|
// S% - The estimated Democratic seat share fraction @ statewide Vf
|
|
@@ -90098,6 +90489,13 @@ function estBias(estSeatShare, bestSeatShare) {
|
|
|
90098
90489
|
return U.trim(Math.abs(estSeatShare - bestSeatShare));
|
|
90099
90490
|
}
|
|
90100
90491
|
exports.estBias = estBias;
|
|
90492
|
+
// UE# - The estimated # of unearned seats
|
|
90493
|
+
// UE_# from http://bit.ly/2Fcuf4q
|
|
90494
|
+
function estUnearnedSeats(best, probable) {
|
|
90495
|
+
// NOTE - + values = unearned R seats; – values = unearned D seats
|
|
90496
|
+
return U.trim(best - probable);
|
|
90497
|
+
}
|
|
90498
|
+
exports.estUnearnedSeats = estUnearnedSeats;
|
|
90101
90499
|
// ESTIMATE RESPONSIVENESS ("COMPETITIVE")
|
|
90102
90500
|
// R# - Estimate responsiveness at the statewide vote share
|
|
90103
90501
|
function estResponsiveness(Vf, inferredSVpoints) {
|
|
@@ -90152,6 +90550,14 @@ function estResponsiveDistrictsShare(Rd, N) {
|
|
|
90152
90550
|
}
|
|
90153
90551
|
exports.estResponsiveDistrictsShare = estResponsiveDistrictsShare;
|
|
90154
90552
|
// ESTIMATE COMPETITIVENESS (ENHANCED)
|
|
90553
|
+
// C - Count the # of competitive districts, defined as v in [45–55%]
|
|
90554
|
+
function countCompetitiveDistricts(VfArray) {
|
|
90555
|
+
return U.trim(U.sumArray(VfArray.map(v => isCompetitive(v))));
|
|
90556
|
+
}
|
|
90557
|
+
exports.countCompetitiveDistricts = countCompetitiveDistricts;
|
|
90558
|
+
function isCompetitive(v) {
|
|
90559
|
+
return ((v >= C.competitiveRange()[C.BEG]) && (v <= C.competitiveRange()[C.END])) ? 1 : 0;
|
|
90560
|
+
}
|
|
90155
90561
|
// Cd - The estimated # of competitive districts
|
|
90156
90562
|
function estCompetitiveDistricts(VfArray) {
|
|
90157
90563
|
return U.trim(U.sumArray(VfArray.map(v => estDistrictCompetitiveness(v))));
|
|
@@ -90160,8 +90566,14 @@ exports.estCompetitiveDistricts = estCompetitiveDistricts;
|
|
|
90160
90566
|
// Re-scale a Democratic vote share to the competitive range (e.g., 45–55%).
|
|
90161
90567
|
function estDistrictCompetitiveness(Vf) {
|
|
90162
90568
|
const _normalizer = new normalize_1.Normalizer(Vf);
|
|
90163
|
-
|
|
90164
|
-
|
|
90569
|
+
// The end points of a compressed probability distribution
|
|
90570
|
+
// NOTE - These aren't the points where races start or stop being contested,
|
|
90571
|
+
// just the end points of a distribution that yields the desired behavior
|
|
90572
|
+
// in the typical competitive range [45-55%].
|
|
90573
|
+
const distBeg = C.competitiveDistribution()[C.BEG];
|
|
90574
|
+
const distEnd = C.competitiveDistribution()[C.END];
|
|
90575
|
+
_normalizer.clip(distBeg, distEnd);
|
|
90576
|
+
_normalizer.unitize(distBeg, distEnd);
|
|
90165
90577
|
const dC = estDistrictResponsiveness(_normalizer.wipNum);
|
|
90166
90578
|
return dC;
|
|
90167
90579
|
}
|
|
@@ -90171,9 +90583,30 @@ function estCompetitiveDistrictsShare(Cd, N) {
|
|
|
90171
90583
|
return U.trim(Cd / N);
|
|
90172
90584
|
}
|
|
90173
90585
|
exports.estCompetitiveDistrictsShare = estCompetitiveDistrictsShare;
|
|
90586
|
+
// Md - The estimated # of "marginal" districts in and around the likely FPTP
|
|
90587
|
+
// seats & the best seat split that are competitive.
|
|
90588
|
+
function estMarginalCompetitiveDistricts(Mrange, VfArray) {
|
|
90589
|
+
const minId = Mrange[C.BEG];
|
|
90590
|
+
const maxId = Mrange[C.END];
|
|
90591
|
+
// Sort the array values, and subset it to those districts
|
|
90592
|
+
// NOTE - I'm *not* keeping track of the district indexes right now
|
|
90593
|
+
let subsetVfArray = VfArray.sort().slice(minId - 1, maxId);
|
|
90594
|
+
// Est. competitive districts on that array
|
|
90595
|
+
const Md = U.sumArray(subsetVfArray.map(v => estDistrictCompetitiveness(v)));
|
|
90596
|
+
return U.trim(Md);
|
|
90597
|
+
}
|
|
90598
|
+
exports.estMarginalCompetitiveDistricts = estMarginalCompetitiveDistricts;
|
|
90174
90599
|
// Md% - The estimated competitiveness of the "marginal" districts in and around
|
|
90175
90600
|
// the likely FPTP seats & the best seat split as a fraction
|
|
90176
|
-
function estMarginalCompetitiveShare(
|
|
90601
|
+
function estMarginalCompetitiveShare(Md, Mrange) {
|
|
90602
|
+
const minId = Mrange[C.BEG];
|
|
90603
|
+
const maxId = Mrange[C.END];
|
|
90604
|
+
// Est. competitive district share on that result
|
|
90605
|
+
const MdShare = U.trim(estCompetitiveDistrictsShare(Md, maxId - minId + 1));
|
|
90606
|
+
return U.trim(MdShare);
|
|
90607
|
+
}
|
|
90608
|
+
exports.estMarginalCompetitiveShare = estMarginalCompetitiveShare;
|
|
90609
|
+
function findMarginalDistricts(Vf, VfArray, N) {
|
|
90177
90610
|
const bestS = bestSeats(N, Vf);
|
|
90178
90611
|
const fptpS = estFPTPSeats(VfArray);
|
|
90179
90612
|
// Find the marginal districts IDs (indexed 1–N)
|
|
@@ -90192,137 +90625,9 @@ function estMarginalCompetitiveShare(Vf, VfArray, N) {
|
|
|
90192
90625
|
minId = Math.max((N - bestS) - 1, 1);
|
|
90193
90626
|
maxId = Math.min((N - bestS) + 1, N);
|
|
90194
90627
|
}
|
|
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);
|
|
90628
|
+
return [minId, maxId];
|
|
90204
90629
|
}
|
|
90205
|
-
exports.
|
|
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
|
-
}
|
|
90324
|
-
}
|
|
90325
|
-
exports.Normalizer = Normalizer;
|
|
90630
|
+
exports.findMarginalDistricts = findMarginalDistricts;
|
|
90326
90631
|
|
|
90327
90632
|
|
|
90328
90633
|
/***/ }),
|
|
@@ -90347,18 +90652,19 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
90347
90652
|
return result;
|
|
90348
90653
|
};
|
|
90349
90654
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90350
|
-
const
|
|
90655
|
+
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
90656
|
+
const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
|
|
90351
90657
|
const compact_1 = __webpack_require__(/*! ./compact */ "./src/compact.ts");
|
|
90352
90658
|
const cohesive_1 = __webpack_require__(/*! ./cohesive */ "./src/cohesive.ts");
|
|
90353
90659
|
const equal_1 = __webpack_require__(/*! ./equal */ "./src/equal.ts");
|
|
90354
|
-
const
|
|
90660
|
+
const partisan_1 = __webpack_require__(/*! ./partisan */ "./src/partisan.ts");
|
|
90355
90661
|
// TODO - Score opportunity for minority representation
|
|
90356
90662
|
function scorePlan(p, overridesJSON) {
|
|
90357
|
-
//
|
|
90663
|
+
// TRADITIONAL DISTRICTING PRINCIPLES ("best") subcategories - compactness, splitting, population deviation
|
|
90358
90664
|
// Compactness
|
|
90359
90665
|
const reockM = compact_1.doReock(p.compactnessProfile.GeometryByDistrict);
|
|
90360
90666
|
const polsbyM = compact_1.doPolsbyPopper(p.compactnessProfile.GeometryByDistrict);
|
|
90361
|
-
const compactnessScore =
|
|
90667
|
+
const compactnessScore = weightCompactness(reockM.normalized, polsbyM.normalized);
|
|
90362
90668
|
const cS = {
|
|
90363
90669
|
score: compactnessScore,
|
|
90364
90670
|
reock: reockM,
|
|
@@ -90370,51 +90676,77 @@ function scorePlan(p, overridesJSON) {
|
|
|
90370
90676
|
const cT = cohesive_1.totalCounties(CxD);
|
|
90371
90677
|
const countyM = cohesive_1.doCountySplittingReduced(CxD, dT, cT);
|
|
90372
90678
|
const districtM = cohesive_1.doDistrictSplittingReduced(CxD, dT, cT);
|
|
90373
|
-
const splittingScore =
|
|
90679
|
+
const splittingScore = weightSplitting(countyM.normalized, districtM.normalized);
|
|
90374
90680
|
const sS = {
|
|
90375
90681
|
score: splittingScore,
|
|
90376
90682
|
county: countyM,
|
|
90377
90683
|
district: districtM
|
|
90378
90684
|
};
|
|
90379
90685
|
// Population deviation
|
|
90380
|
-
const
|
|
90381
|
-
// Combine
|
|
90382
|
-
const
|
|
90383
|
-
// Populate the "best" scorecard
|
|
90384
|
-
const
|
|
90385
|
-
|
|
90686
|
+
const pdS = equal_1.doPopulationDeviation(p.populationProfile.TotalPopByDistrict, p.populationProfile.targetSize, p.legislativeDistricts);
|
|
90687
|
+
// Combine traditional principles into a score to compare plans w/in a state
|
|
90688
|
+
const tpScore = weightTradtionalPrinciples(cS.score, sS.score, pdS.normalized, 1 /* WithinAState */);
|
|
90689
|
+
// Populate the "best" traditional principles scorecard
|
|
90690
|
+
const tpS = {
|
|
90691
|
+
score: tpScore,
|
|
90386
90692
|
compactness: cS,
|
|
90387
90693
|
splitting: sS,
|
|
90388
|
-
populationDeviation:
|
|
90694
|
+
populationDeviation: pdS
|
|
90389
90695
|
};
|
|
90390
|
-
//
|
|
90391
|
-
|
|
90392
|
-
|
|
90393
|
-
|
|
90394
|
-
|
|
90395
|
-
const score = Math.round(((S.FAIR_WEIGHT * pS.score) + (S.BEST_WEIGHT * bS.BestScore)) / (S.FAIR_WEIGHT + S.BEST_WEIGHT));
|
|
90696
|
+
// PARTISAN ("fair") subcategories - bias, impact, & competitiveness (plus lots of supporting measures)
|
|
90697
|
+
const pS = partisan_1.scorePartisan(p.partisanProfile.statewideVf, p.partisanProfile.VfArray);
|
|
90698
|
+
// Combine the partisan/partisan & traditional principles/best scores into an overall
|
|
90699
|
+
// score for comparing plans w/in a state
|
|
90700
|
+
const score = weightBestFair(pS.score2, tpS.score, 1 /* WithinAState */);
|
|
90396
90701
|
// Roll up an overall scorecard
|
|
90397
90702
|
const scorecard = {
|
|
90398
|
-
|
|
90399
|
-
|
|
90400
|
-
|
|
90703
|
+
partisan: pS,
|
|
90704
|
+
traditionalPrinciples: tpS,
|
|
90705
|
+
score: score
|
|
90401
90706
|
};
|
|
90402
90707
|
return scorecard;
|
|
90403
90708
|
}
|
|
90404
90709
|
exports.scorePlan = scorePlan;
|
|
90710
|
+
// HELPERS
|
|
90711
|
+
function weightCompactness(rS, ppS) {
|
|
90712
|
+
const rW = C.reockWeight();
|
|
90713
|
+
const ppW = C.polsbyWeight();
|
|
90714
|
+
const score = Math.round(((rS * rW) + (ppS * ppW)) / (rW + ppW));
|
|
90715
|
+
return score;
|
|
90716
|
+
}
|
|
90717
|
+
exports.weightCompactness = weightCompactness;
|
|
90718
|
+
function weightSplitting(csS, dsS) {
|
|
90719
|
+
const csW = C.countySplittingWeight();
|
|
90720
|
+
const dsW = C.districtSplittingWeight();
|
|
90721
|
+
const score = Math.round(((csS * csW) + (dsS * dsW)) / (csW + dsW));
|
|
90722
|
+
return score;
|
|
90723
|
+
}
|
|
90724
|
+
exports.weightSplitting = weightSplitting;
|
|
90725
|
+
function weightTradtionalPrinciples(cS, sS, pdS, context) {
|
|
90726
|
+
const cW = C.compactnessWeight(context);
|
|
90727
|
+
const sW = C.splittingWeight(context);
|
|
90728
|
+
const pdW = C.popdevWeight(context);
|
|
90729
|
+
const score = Math.round(((cS * cW) + (sS * sW) + (pdS * pdW)) / (cW + sW + pdW));
|
|
90730
|
+
return score;
|
|
90731
|
+
}
|
|
90732
|
+
exports.weightTradtionalPrinciples = weightTradtionalPrinciples;
|
|
90733
|
+
function weightBestFair(pS, tpS, context) {
|
|
90734
|
+
const pW = C.partisanWeight(context);
|
|
90735
|
+
const tpW = C.traditionalPrinciplesWeight(context);
|
|
90736
|
+
const score = Math.round(((pS * pW) + (tpS * tpW)) / (pW + tpW));
|
|
90737
|
+
return score;
|
|
90738
|
+
}
|
|
90739
|
+
exports.weightBestFair = weightBestFair;
|
|
90740
|
+
function printScorecardHeader() {
|
|
90741
|
+
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$, $$$');
|
|
90742
|
+
}
|
|
90743
|
+
exports.printScorecardHeader = printScorecardHeader;
|
|
90744
|
+
function printScorecardRow(xx, name, N, Vf, s) {
|
|
90745
|
+
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', 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.score);
|
|
90746
|
+
}
|
|
90747
|
+
exports.printScorecardRow = printScorecardRow;
|
|
90405
90748
|
|
|
90406
90749
|
|
|
90407
|
-
/***/ }),
|
|
90408
|
-
|
|
90409
|
-
/***/ "./src/settings.json":
|
|
90410
|
-
/*!***************************!*\
|
|
90411
|
-
!*** ./src/settings.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
90750
|
/***/ }),
|
|
90419
90751
|
|
|
90420
90752
|
/***/ "./src/settings.ts":
|
|
@@ -90429,16 +90761,13 @@ module.exports = JSON.parse("{\"fair\":{\"unbiased\":{\"worst\":0.1,\"best\":0,\
|
|
|
90429
90761
|
//
|
|
90430
90762
|
// GLOBAL CONSTANTS
|
|
90431
90763
|
//
|
|
90432
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
90433
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
90434
|
-
};
|
|
90435
90764
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90436
90765
|
// Normalized scores [0-100]
|
|
90437
90766
|
exports.NORMALIZED_RANGE = 100;
|
|
90438
90767
|
// Square deviations from the ideal
|
|
90439
90768
|
exports.DISTANCE_WEIGHT = 2;
|
|
90440
90769
|
// Out of range message
|
|
90441
|
-
exports.OUT_OF_RANGE_MSG = "
|
|
90770
|
+
exports.OUT_OF_RANGE_MSG = "%f out of range";
|
|
90442
90771
|
// A small delta to use when testing ranges of values
|
|
90443
90772
|
exports.EPSILON = 1 / Math.pow(10, 6);
|
|
90444
90773
|
// Keep 4 decimals for fractions [0.0–1.0], i.e., 2 for %'s [0–100]
|
|
@@ -90446,62 +90775,6 @@ exports.PRECISION = 4;
|
|
|
90446
90775
|
// "Roughly equal" = average census block size / 2
|
|
90447
90776
|
exports.AVERAGE_BLOCK_SIZE = 30;
|
|
90448
90777
|
exports.EQUAL_TOLERANCE = exports.AVERAGE_BLOCK_SIZE / 2;
|
|
90449
|
-
// SCORING PARAMETERS
|
|
90450
|
-
const settings_json_1 = __importDefault(__webpack_require__(/*! ./settings.json */ "./src/settings.json"));
|
|
90451
|
-
// TODO - DELETE
|
|
90452
|
-
function readOverrides() {
|
|
90453
|
-
return undefined;
|
|
90454
|
-
}
|
|
90455
|
-
const overrides = readOverrides();
|
|
90456
|
-
// SETTINGS FOR PARTISAN SCORING
|
|
90457
|
-
exports.MIN_CONTESTED = overrides ? overrides.fair.competitive.min : settings_json_1.default.fair.competitive.min;
|
|
90458
|
-
exports.MAX_CONTESTED = overrides ? overrides.fair.competitive.max : settings_json_1.default.fair.competitive.max;
|
|
90459
|
-
// SCALES FOR NORMALIZING RAW VALUES
|
|
90460
|
-
// Compactness scales
|
|
90461
|
-
exports.REOCK_WORST = overrides ? overrides.best.compact.reock.worst : settings_json_1.default.best.compact.reock.worst;
|
|
90462
|
-
exports.REOCK_BEST = overrides ? overrides.best.compact.reock.best : settings_json_1.default.best.compact.reock.best;
|
|
90463
|
-
exports.POLSBY_WORST = overrides ? overrides.best.compact.polsby.worst : settings_json_1.default.best.compact.polsby.worst;
|
|
90464
|
-
exports.POLSBY_BEST = overrides ? overrides.best.compact.polsby.best : settings_json_1.default.best.compact.polsby.best;
|
|
90465
|
-
// County-District splitting scales (not inverted)
|
|
90466
|
-
exports.COUNTY_BEST = overrides ? overrides.best.cohesive.county.best : settings_json_1.default.best.cohesive.county.best;
|
|
90467
|
-
exports.COUNTY_WORST = overrides ? overrides.best.cohesive.county.worst : settings_json_1.default.best.cohesive.county.worst;
|
|
90468
|
-
exports.ALLOWABLE_SPLITS_MULTIPLIER = overrides ? overrides.best.cohesive.county.allowableSplitsMultiplier : settings_json_1.default.best.cohesive.county.allowableSplitsMultiplier;
|
|
90469
|
-
exports.DISTRICT_BEST = overrides ? overrides.best.cohesive.district.best : settings_json_1.default.best.cohesive.district.best;
|
|
90470
|
-
exports.DISTRICT_WORST = overrides ? overrides.best.cohesive.district.worst : settings_json_1.default.best.cohesive.district.worst;
|
|
90471
|
-
// Population deviation thresholds & scales (inverted)
|
|
90472
|
-
exports.POPDEV_WORST = overrides ? overrides.best.equal.worst : settings_json_1.default.best.equal.worst;
|
|
90473
|
-
exports.POPDEV_BEST = overrides ? overrides.best.equal.best : settings_json_1.default.best.equal.best;
|
|
90474
|
-
exports.POPEQ_MIN = 1.0 - exports.POPDEV_WORST;
|
|
90475
|
-
exports.POPEQ_MAX = 1.0 - exports.POPDEV_BEST;
|
|
90476
|
-
exports.POPDEV_WORST_LD = overrides ? overrides.best.equal.stateLeg.worst : settings_json_1.default.best.equal.stateLeg.worst;
|
|
90477
|
-
exports.POPDEV_BEST_LD = ((exports.POPDEV_BEST / exports.POPDEV_WORST) * exports.POPDEV_WORST_LD);
|
|
90478
|
-
exports.POPEQ_MIN_LD = 1.0 - exports.POPDEV_WORST_LD;
|
|
90479
|
-
exports.POPEQ_MAX_LD = 1.0 - exports.POPDEV_BEST_LD;
|
|
90480
|
-
// Bias & competitiveness scales
|
|
90481
|
-
// TODO - SCORE:
|
|
90482
|
-
// * How wide a range do we want for bias? 10%? 20%?
|
|
90483
|
-
// * Do we want it to be state-specific? E.g., one seat?
|
|
90484
|
-
exports.BIAS_WORST = overrides ? overrides.fair.unbiased.worst : settings_json_1.default.fair.unbiased.worst;
|
|
90485
|
-
exports.BIAS_BEST = overrides ? overrides.fair.unbiased.best : settings_json_1.default.fair.unbiased.best;
|
|
90486
|
-
// The range for raw overall competitiveness values
|
|
90487
|
-
exports.COMPETITIVE_WORST = overrides ? overrides.fair.competitive.worst : settings_json_1.default.fair.competitive.worst;
|
|
90488
|
-
exports.COMPETITIVE_BEST = overrides ? overrides.fair.competitive.best : settings_json_1.default.fair.competitive.best;
|
|
90489
|
-
// WEIGHTS FOR COMBINING NORMALIZED VALUES
|
|
90490
|
-
exports.REOCK_WEIGHT = overrides ? overrides.best.compact.reock.weight : settings_json_1.default.best.compact.reock.weight;
|
|
90491
|
-
exports.POLSBY_WEIGHT = overrides ? overrides.best.compact.polsby.weight : settings_json_1.default.best.compact.polsby.weight;
|
|
90492
|
-
exports.COUNTY_WEIGHT = overrides ? overrides.best.cohesive.county.weight : settings_json_1.default.best.cohesive.county.weight;
|
|
90493
|
-
exports.DISTRICT_WEIGHT = overrides ? overrides.best.cohesive.district.weight : settings_json_1.default.best.cohesive.district.weight;
|
|
90494
|
-
exports.COMPACTNESS_WEIGHT = overrides ? overrides.best.compact.weight : settings_json_1.default.best.compact.weight;
|
|
90495
|
-
exports.SPLITTING_WEIGHT = overrides ? overrides.best.cohesive.weight : settings_json_1.default.best.cohesive.weight;
|
|
90496
|
-
exports.POPDEV_WEIGHT = overrides ? overrides.best.equal.weight : settings_json_1.default.best.equal.weight;
|
|
90497
|
-
// TODO - SCORE: What kind of relative weighting do we want between marginal and
|
|
90498
|
-
// overall competitiveness?
|
|
90499
|
-
exports.MARGINAL_WEIGHT = overrides ? overrides.fair.competitive.marginal : settings_json_1.default.fair.competitive.marginal;
|
|
90500
|
-
exports.OVERALL_WEIGHT = overrides ? overrides.fair.competitive.overall : settings_json_1.default.fair.competitive.overall;
|
|
90501
|
-
exports.BIAS_WEIGHT = overrides ? overrides.fair.unbiased.weight : settings_json_1.default.fair.unbiased.weight;
|
|
90502
|
-
exports.COMPETITIVE_WEIGHT = overrides ? overrides.fair.competitive.weight : settings_json_1.default.fair.competitive.weight;
|
|
90503
|
-
exports.FAIR_WEIGHT = overrides ? overrides.fair.weight : settings_json_1.default.fair.weight;
|
|
90504
|
-
exports.BEST_WEIGHT = overrides ? overrides.best.weight : settings_json_1.default.best.weight;
|
|
90505
90778
|
|
|
90506
90779
|
|
|
90507
90780
|
/***/ }),
|
|
@@ -90522,9 +90795,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
90522
90795
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
90523
90796
|
};
|
|
90524
90797
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
90525
|
-
const sample_profile_json_1 = __importDefault(__webpack_require__(/*! ../testdata/
|
|
90798
|
+
const sample_profile_json_1 = __importDefault(__webpack_require__(/*! ../testdata/samples/sample-profile.json */ "./testdata/samples/sample-profile.json"));
|
|
90526
90799
|
exports.sampleProfile = sample_profile_json_1.default;
|
|
90527
|
-
const sample_scorecard_json_1 = __importDefault(__webpack_require__(/*! ../testdata/
|
|
90800
|
+
const sample_scorecard_json_1 = __importDefault(__webpack_require__(/*! ../testdata/samples/sample-scorecard.json */ "./testdata/samples/sample-scorecard.json"));
|
|
90528
90801
|
exports.sampleScorecard = sample_scorecard_json_1.default;
|
|
90529
90802
|
|
|
90530
90803
|
|
|
@@ -90606,10 +90879,10 @@ exports.deepCopy = deepCopy;
|
|
|
90606
90879
|
|
|
90607
90880
|
/***/ }),
|
|
90608
90881
|
|
|
90609
|
-
/***/ "./testdata/
|
|
90610
|
-
|
|
90611
|
-
!*** ./testdata/
|
|
90612
|
-
|
|
90882
|
+
/***/ "./testdata/samples/sample-profile.json":
|
|
90883
|
+
/*!**********************************************!*\
|
|
90884
|
+
!*** ./testdata/samples/sample-profile.json ***!
|
|
90885
|
+
\**********************************************/
|
|
90613
90886
|
/*! exports provided: state, planName, nDistricts, nCounties, legislativeDistricts, populationProfile, compactnessProfile, splittingProfile, partisanProfile, demographicProfile, default */
|
|
90614
90887
|
/***/ (function(module) {
|
|
90615
90888
|
|
|
@@ -90617,14 +90890,14 @@ module.exports = JSON.parse("{\"state\":\"NC\",\"planName\":\"Sample profile\",\
|
|
|
90617
90890
|
|
|
90618
90891
|
/***/ }),
|
|
90619
90892
|
|
|
90620
|
-
/***/ "./testdata/
|
|
90621
|
-
|
|
90622
|
-
!*** ./testdata/
|
|
90623
|
-
|
|
90624
|
-
/*! exports provided: score,
|
|
90893
|
+
/***/ "./testdata/samples/sample-scorecard.json":
|
|
90894
|
+
/*!************************************************!*\
|
|
90895
|
+
!*** ./testdata/samples/sample-scorecard.json ***!
|
|
90896
|
+
\************************************************/
|
|
90897
|
+
/*! exports provided: score, traditionalPrinciples, partisan, default */
|
|
90625
90898
|
/***/ (function(module) {
|
|
90626
90899
|
|
|
90627
|
-
module.exports = JSON.parse("{\"score\":5,\"
|
|
90900
|
+
module.exports = JSON.parse("{\"score\":5,\"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}}},\"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}}");
|
|
90628
90901
|
|
|
90629
90902
|
/***/ })
|
|
90630
90903
|
|
|
@@ -100410,6 +100683,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
100410
100683
|
return result;
|
|
100411
100684
|
};
|
|
100412
100685
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
100686
|
+
const Score = __importStar(__webpack_require__(/*! @dra2020/dra-score */ "./node_modules/@dra2020/dra-score/dist/dra-score.bundle.js"));
|
|
100413
100687
|
const preprocess_1 = __webpack_require__(/*! ./preprocess */ "./src/preprocess.ts");
|
|
100414
100688
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
100415
100689
|
const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
|
|
@@ -100603,9 +100877,13 @@ class AnalyticsSession {
|
|
|
100603
100877
|
return 1 - this.testScales[4 /* PopulationDeviation */]['scale'][0];
|
|
100604
100878
|
}
|
|
100605
100879
|
else {
|
|
100880
|
+
// NOTE - This assumes the plan has been profiled
|
|
100881
|
+
const scorer = new Score.Scorer();
|
|
100882
|
+
const popdev = scorer.populationDeviationThreshold(this.legislativeDistricts);
|
|
100883
|
+
return popdev;
|
|
100606
100884
|
// TODO - SCORE: Temporary HACK. Query dra-score for threshold.
|
|
100607
100885
|
// NOTE - The plan may not have been scored yet, i.e., no scorecard yet.
|
|
100608
|
-
return 1 - this.testScales[
|
|
100886
|
+
// return 1 - this.testScales[T.Test.PopulationDeviation]['scale'][0];
|
|
100609
100887
|
}
|
|
100610
100888
|
}
|
|
100611
100889
|
}
|
|
@@ -102815,7 +103093,9 @@ function doAnalyzePostProcessing(s, bLog = false) {
|
|
|
102815
103093
|
// Just populate the normalized population deviation score in the test
|
|
102816
103094
|
const scorecard = s._scorecard;
|
|
102817
103095
|
let test = s.getTest(4 /* PopulationDeviation */);
|
|
102818
|
-
test['normalizedScore'] = scorecard.
|
|
103096
|
+
test['normalizedScore'] = scorecard.traditionalPrinciples.populationDeviation.normalized;
|
|
103097
|
+
// TODO - DELETE
|
|
103098
|
+
// test['normalizedScore'] = scorecard.best.populationDeviation.normalized;
|
|
102819
103099
|
}
|
|
102820
103100
|
// Derive secondary tests
|
|
102821
103101
|
analyze_1.doDeriveSecondaryTests(s, bLog);
|
|
@@ -102861,7 +103141,7 @@ function profilePlan(s, bLog = false) {
|
|
|
102861
103141
|
const geoPropsByDistrict = makeArrayOfGeoProps(s, bLog);
|
|
102862
103142
|
const splits = makeNakedCxD(s);
|
|
102863
103143
|
const summaryRow = s.districts.numberOfRows() - 1;
|
|
102864
|
-
const statewideVf = s.districts.statistics[D.DistrictField.DemPct][summaryRow];
|
|
103144
|
+
const statewideVf = U.trim(s.districts.statistics[D.DistrictField.DemPct][summaryRow], 6);
|
|
102865
103145
|
const vpiArray = U.deepCopy(s.districts.statistics[D.DistrictField.DemPct].slice(1, -1));
|
|
102866
103146
|
const demographicsByDistrict = makeArrayOfDemographics(s);
|
|
102867
103147
|
const profile = {
|
|
@@ -102945,11 +103225,14 @@ function scorePlan(s, p, bLog = false, overridesJSON) {
|
|
|
102945
103225
|
// calling sequence.
|
|
102946
103226
|
let test = s.getTest(4 /* PopulationDeviation */);
|
|
102947
103227
|
// TODO - SCORE: U.trim(popDev)???
|
|
103228
|
+
// const popDev = scorecard.best.populationDeviation.raw;
|
|
102948
103229
|
// Get the raw population deviation
|
|
102949
|
-
const popDev = scorecard.
|
|
103230
|
+
const popDev = scorecard.traditionalPrinciples.populationDeviation.raw;
|
|
102950
103231
|
// Populate the test entry
|
|
102951
103232
|
test['score'] = popDev;
|
|
102952
|
-
test['details'] = { 'maxDeviation': scorecard.
|
|
103233
|
+
test['details'] = { 'maxDeviation': scorecard.traditionalPrinciples.populationDeviation.notes['maxDeviation'] };
|
|
103234
|
+
// TODO - DELETE
|
|
103235
|
+
// test['details'] = { 'maxDeviation': scorecard.best.populationDeviation.notes['maxDeviation'] };
|
|
102953
103236
|
// Populate the N+1 summary "district" in district.statistics
|
|
102954
103237
|
let totalPop = s.districts.statistics[D.DistrictField.TotalPop];
|
|
102955
103238
|
let popDevPct = s.districts.statistics[D.DistrictField.PopDevPct];
|