@dra2020/district-analytics 6.0.1 → 6.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 +177 -96
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +13 -0
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/_api.d.ts +1 -0
- package/dist/src/types.d.ts +8 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -89102,7 +89102,7 @@ exports.scorePolsbyPopper = scorePolsbyPopper;
|
|
|
89102
89102
|
/*! exports provided: partisan, minority, traditionalPrinciples, default */
|
|
89103
89103
|
/***/ (function(module) {
|
|
89104
89104
|
|
|
89105
|
-
module.exports = JSON.parse("{\"partisan\":{\"bias\":{\"range\":[0,0.2],\"weight\":[50,80]},\"impact\":{\"weight\":[50,0],\"threshold\":4},\"competitiveness\":{\"
|
|
89105
|
+
module.exports = JSON.parse("{\"partisan\":{\"bias\":{\"range\":[0,0.2],\"weight\":[50,80]},\"impact\":{\"weight\":[50,0],\"threshold\":4},\"competitiveness\":{\"range\":[0,0.75],\"distribution\":[0.25,0.75],\"simpleRange\":[0.45,0.55],\"weight\":[0,20]},\"bonus\":2,\"weight\":[100,80]},\"minority\":{\"range\":[0.37,0.5],\"distribution\":[0.25,0.75],\"shift\":0.15,\"coalition\":{\"weight\":0.5},\"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.26,1.68],[1.09,1.45]],\"weight\":50},\"district\":{\"range\":[[1.26,1.68],[1.09,1.45]],\"weight\":50},\"weight\":[0,50]},\"popdev\":{\"range\":[[0.0075,0.002],[0.1,-1]],\"weight\":[0,0]},\"weight\":[0,20]}}");
|
|
89106
89106
|
|
|
89107
89107
|
/***/ }),
|
|
89108
89108
|
|
|
@@ -89160,8 +89160,9 @@ function competitivenessWeight(context, overridesJSON) {
|
|
|
89160
89160
|
return cW;
|
|
89161
89161
|
}
|
|
89162
89162
|
exports.competitivenessWeight = competitivenessWeight;
|
|
89163
|
+
// COMPETITIVENESS - The simple user-facing range, i.e., 45–55%.
|
|
89163
89164
|
function competitiveRange(overridesJSON) {
|
|
89164
|
-
const range = config_json_1.default.partisan.competitiveness.
|
|
89165
|
+
const range = config_json_1.default.partisan.competitiveness.simpleRange;
|
|
89165
89166
|
return range;
|
|
89166
89167
|
}
|
|
89167
89168
|
exports.competitiveRange = competitiveRange;
|
|
@@ -89170,26 +89171,29 @@ function competitiveDistribution(overridesJSON) {
|
|
|
89170
89171
|
return dist;
|
|
89171
89172
|
}
|
|
89172
89173
|
exports.competitiveDistribution = competitiveDistribution;
|
|
89174
|
+
// COMPETITIVENESS - The more complex internal range for normalizing Cdf.
|
|
89175
|
+
// COMPETITIVENESS - 06/23/2020 - As part of relaxing the competitive range, I
|
|
89176
|
+
// I changed this max from 0.67 to 0.75.
|
|
89173
89177
|
function overallCompetitivenessRange(overridesJSON) {
|
|
89174
|
-
const range = config_json_1.default.partisan.competitiveness.
|
|
89178
|
+
const range = config_json_1.default.partisan.competitiveness.range;
|
|
89175
89179
|
return range;
|
|
89176
89180
|
}
|
|
89177
89181
|
exports.overallCompetitivenessRange = overallCompetitivenessRange;
|
|
89178
|
-
function marginalCompetitivenessRange(overridesJSON)
|
|
89179
|
-
|
|
89180
|
-
|
|
89181
|
-
|
|
89182
|
-
|
|
89183
|
-
function marginalCompetitivenessWeight(overridesJSON)
|
|
89184
|
-
|
|
89185
|
-
|
|
89186
|
-
|
|
89187
|
-
|
|
89188
|
-
function overallCompetitivenessWeight(overridesJSON)
|
|
89189
|
-
|
|
89190
|
-
|
|
89191
|
-
|
|
89192
|
-
|
|
89182
|
+
// export function marginalCompetitivenessRange(overridesJSON?: any): number[]
|
|
89183
|
+
// {
|
|
89184
|
+
// const range = config.partisan.competitiveness.marginal.range;
|
|
89185
|
+
// return range;
|
|
89186
|
+
// }
|
|
89187
|
+
// export function marginalCompetitivenessWeight(overridesJSON?: any): number
|
|
89188
|
+
// {
|
|
89189
|
+
// const mcW = config.partisan.competitiveness.marginal.weight;
|
|
89190
|
+
// return mcW;
|
|
89191
|
+
// }
|
|
89192
|
+
// export function overallCompetitivenessWeight(overridesJSON?: any): number
|
|
89193
|
+
// {
|
|
89194
|
+
// const ocW = config.partisan.competitiveness.overall.weight;
|
|
89195
|
+
// return ocW;
|
|
89196
|
+
// }
|
|
89193
89197
|
// MINORITY
|
|
89194
89198
|
function minorityOpportunityRange(overridesJSON) {
|
|
89195
89199
|
const range = config_json_1.default.minority.range;
|
|
@@ -89396,6 +89400,8 @@ function __export(m) {
|
|
|
89396
89400
|
}
|
|
89397
89401
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
89398
89402
|
__export(__webpack_require__(/*! ./_api */ "./src/_api.ts"));
|
|
89403
|
+
var partisan_1 = __webpack_require__(/*! ./partisan */ "./src/partisan.ts");
|
|
89404
|
+
exports.estSeatProbability = partisan_1.estSeatProbability;
|
|
89399
89405
|
|
|
89400
89406
|
|
|
89401
89407
|
/***/ }),
|
|
@@ -89431,39 +89437,63 @@ function evalMinorityOpportunity(p, bLog = false) {
|
|
|
89431
89437
|
const RWins = VfArray.filter(x => x <= 0.5); // Ties credited to R's
|
|
89432
89438
|
const averageDVf = (DWins.length > 0) ? U.avgArray(DWins) : undefined;
|
|
89433
89439
|
const averageRVf = (RWins.length > 0) ? U.avgArray(RWins) : undefined;
|
|
89434
|
-
// Determine proportional minority districts by demographic (ignore 'White')
|
|
89435
|
-
const districtsByDemo = calcDistrictsByDemo(p.demographicProfile.stateMfArray.slice(1), p.nDistricts);
|
|
89436
89440
|
// Initialize arrays for results
|
|
89437
89441
|
const offset = 1; // Don't process 'White'
|
|
89438
|
-
const nDemos = 6 /*
|
|
89439
|
-
const
|
|
89442
|
+
const nDemos = 6 /* Total */; // Ditto
|
|
89443
|
+
// const nDemos = T.DemographicField.Native + 1 - offset; // Ditto
|
|
89440
89444
|
const demosByDistrict = p.demographicProfile.mfArrayByDistrict;
|
|
89441
89445
|
// Initialize the demographic buckets
|
|
89442
|
-
|
|
89446
|
+
// COMPETITIVENESS - 06/23/2020 - And populate the statewide values.
|
|
89447
|
+
// Get the statewide minority VAP/CVAP % (ignore 'White')
|
|
89448
|
+
const vapPctArray = p.demographicProfile.stateMfArray.slice(1);
|
|
89449
|
+
// Determine proportional minority districts by demographic (ignoring'White')
|
|
89450
|
+
const districtsByDemo = calcDistrictsByDemo(vapPctArray, p.nDistricts);
|
|
89451
|
+
let bucketsByDemo = new Array(nDemos + 1); // Add a row for the 'Total' row
|
|
89452
|
+
let totalProportional = 0;
|
|
89443
89453
|
for (let j = 0; j < nDemos; j++) {
|
|
89444
|
-
|
|
89445
|
-
|
|
89446
|
-
|
|
89454
|
+
const vapPct = vapPctArray[j];
|
|
89455
|
+
const prop = districtsByDemo[j];
|
|
89456
|
+
// Accumulate the proportional values for each demographic, ignoring the first
|
|
89457
|
+
// all Minority demographic.
|
|
89458
|
+
if (j > 0)
|
|
89459
|
+
totalProportional += prop;
|
|
89460
|
+
bucketsByDemo[j] = [0, 0, 0, vapPct, prop]; // Five conceptual columns for each demographic
|
|
89461
|
+
}
|
|
89462
|
+
// Add a 'Total' row
|
|
89463
|
+
bucketsByDemo[nDemos] = [0, 0, 0, 0, totalProportional];
|
|
89464
|
+
let opptyByDemo = U.initArray(nDemos, 0.0); // A state-level value
|
|
89447
89465
|
// For each district
|
|
89448
89466
|
for (let i = 0; i < p.nDistricts; i++) {
|
|
89449
89467
|
// Find the opportunities for minority representation
|
|
89450
89468
|
for (let j = 0; j < nDemos; j++) {
|
|
89451
89469
|
const proportion = U.deepCopy(demosByDistrict[i][j + offset]); // Skip the 'White' entries
|
|
89452
89470
|
if (exceedsMinimumThreshold(proportion)) {
|
|
89453
|
-
// Bucket opportunity districts
|
|
89471
|
+
// Bucket opportunity districts for each demographic
|
|
89454
89472
|
const bucket = (exceedsMaximumThreshold(proportion)) ? 1 : 0;
|
|
89455
89473
|
bucketsByDemo[j][bucket] += 1;
|
|
89474
|
+
bucketsByDemo[j][2 /* Total */] += 1;
|
|
89456
89475
|
// Accumulate seat probabilities
|
|
89457
89476
|
opptyByDemo[j] += estMinorityOpportunity(proportion);
|
|
89477
|
+
// Also accumulate the 'Total' row summing the opportunities for each demographic,
|
|
89478
|
+
// ignoring the first all Minority demographic.
|
|
89479
|
+
if (j > 0) {
|
|
89480
|
+
bucketsByDemo[nDemos][bucket] += 1;
|
|
89481
|
+
bucketsByDemo[nDemos][2 /* Total */] += 1;
|
|
89482
|
+
}
|
|
89458
89483
|
}
|
|
89459
89484
|
}
|
|
89460
89485
|
}
|
|
89461
89486
|
// The # of opportunity districts for each separate demographic and all minorities
|
|
89462
89487
|
const oD = U.sumArray(opptyByDemo.slice(1)); // Sum individual demos, skipping all minorities
|
|
89463
|
-
const cD = opptyByDemo[0]; // All minorities
|
|
89488
|
+
const cD = opptyByDemo[0 /* Minority */]; // All minorities
|
|
89489
|
+
// const cD: number = opptyByDemo[T.DemographicField.Minority - offset]; // All minorities
|
|
89464
89490
|
// The # of proportional districts for each separate demographic and all minorities
|
|
89465
|
-
const pOd =
|
|
89466
|
-
const pCd =
|
|
89491
|
+
const pOd = bucketsByDemo[6 /* Total */][4 /* PropSeats */];
|
|
89492
|
+
const pCd = bucketsByDemo[0 /* Minority */][4 /* PropSeats */];
|
|
89493
|
+
// const pOd: number = bucketsByDemo[T.DemographicField.Total - 1][T.PivotField.PropSeats];
|
|
89494
|
+
// const pCd: number = bucketsByDemo[T.DemographicField.Minority - 1][T.PivotField.PropSeats];
|
|
89495
|
+
// const pOd: number = U.sumArray(districtsByDemo.slice(1)); // Sum individual demos, skipping all minorities
|
|
89496
|
+
// const pCd: number = districtsByDemo[0]; // All minorities
|
|
89467
89497
|
// Score opportunity
|
|
89468
89498
|
const score = scoreMinority(oD, pOd, cD, pCd);
|
|
89469
89499
|
let mS = {
|
|
@@ -89472,9 +89502,9 @@ function evalMinorityOpportunity(p, bLog = false) {
|
|
|
89472
89502
|
averageRVf: averageRVf,
|
|
89473
89503
|
bucketsByDemographic: bucketsByDemo
|
|
89474
89504
|
},
|
|
89475
|
-
proportionalOpportunities: pOd,
|
|
89505
|
+
// proportionalOpportunities: pOd,
|
|
89476
89506
|
opportunityDistricts: oD,
|
|
89477
|
-
proportionalCoalitions: pCd,
|
|
89507
|
+
// proportionalCoalitions: pCd,
|
|
89478
89508
|
coalitionDistricts: cD,
|
|
89479
89509
|
score: score,
|
|
89480
89510
|
details: {}
|
|
@@ -89730,6 +89760,7 @@ function scorePartisan(Vf, VfArray, options) {
|
|
|
89730
89760
|
const bestS = bestSeats(N, Vf);
|
|
89731
89761
|
const bestSf = bestSeatShare(bestS, N);
|
|
89732
89762
|
const fptpS = bAlternateMetrics ? estFPTPSeats(VfArray) : undefined;
|
|
89763
|
+
// NOTE - When not constrained, use the full probability distribution to calc metrics.
|
|
89733
89764
|
const range = bConstrained ? C.competitiveDistribution() : undefined;
|
|
89734
89765
|
const estS = estSeats(VfArray, range);
|
|
89735
89766
|
const estSf = estSeatShare(estS, N);
|
|
@@ -89739,35 +89770,41 @@ function scorePartisan(Vf, VfArray, options) {
|
|
|
89739
89770
|
const impactScore = scoreImpact(unearnedS, Vf, estSf, N);
|
|
89740
89771
|
// Calculate additional alternate metrics for reference
|
|
89741
89772
|
// NOTE - Use the uncompressed seat probability function
|
|
89742
|
-
const
|
|
89773
|
+
const dSVpoints = inferSVpoints(Vf, VfArray, shift);
|
|
89774
|
+
const rSVpoints = invertSVPoints(dSVpoints);
|
|
89775
|
+
// const dSVpoints = bAlternateMetrics ? inferSVpoints(Vf, VfArray, shift) : undefined;
|
|
89743
89776
|
const TOf = bAlternateMetrics ? calcTurnoutBias(Vf, VfArray) : undefined;
|
|
89744
|
-
const Bs50 = bAlternateMetrics ? estPartisanBias(
|
|
89777
|
+
const Bs50 = bAlternateMetrics ? estPartisanBias(dSVpoints, N) : undefined;
|
|
89745
89778
|
const Bs50f = (!(Bs50 === undefined)) ? U.trim(Bs50 / N) : undefined;
|
|
89746
|
-
const Bv50f = bAlternateMetrics ? estVotesBias(
|
|
89779
|
+
const Bv50f = bAlternateMetrics ? estVotesBias(dSVpoints, N) : undefined;
|
|
89747
89780
|
const rvPoints = bAlternateMetrics ? keyRVpoints(VfArray) : undefined;
|
|
89748
89781
|
const decl = bAlternateMetrics ? calcDeclination(VfArray) : undefined;
|
|
89749
|
-
const gSym = bAlternateMetrics ? calcGlobalSymmetry(
|
|
89782
|
+
const gSym = bAlternateMetrics ? calcGlobalSymmetry(dSVpoints, rSVpoints, Bs50f) : undefined;
|
|
89750
89783
|
const EG = bAlternateMetrics ? calcEfficiencyGap(Vf, estSf) : undefined;
|
|
89751
|
-
const BsGf = bAlternateMetrics ? estGeometricSeatsBias(Vf,
|
|
89784
|
+
const BsGf = bAlternateMetrics ? estGeometricSeatsBias(Vf, dSVpoints, rSVpoints) : undefined;
|
|
89752
89785
|
const prop = bAlternateMetrics ? calcDisproportionality(Vf, estSf) : undefined;
|
|
89753
89786
|
const mMs = bAlternateMetrics ? estMeanMedianDifference(VfArray, Vf) : undefined;
|
|
89754
89787
|
const mMd = bAlternateMetrics ? estMeanMedianDifference(VfArray) : undefined;
|
|
89755
89788
|
const LO = bAlternateMetrics ? calcLopsidedOutcomes(VfArray) : undefined;
|
|
89756
89789
|
// Calculate alternate responsiveness metrics for reference
|
|
89757
89790
|
const bigR = bAlternateMetrics ? calcBigR(Vf, estSf) : undefined;
|
|
89758
|
-
const littleR = bAlternateMetrics ? estResponsiveness(Vf,
|
|
89791
|
+
const littleR = bAlternateMetrics ? estResponsiveness(Vf, dSVpoints) : undefined;
|
|
89759
89792
|
const MIR = (bAlternateMetrics && littleR) ? calcMinimalInverseResponsiveness(Vf, littleR) : undefined;
|
|
89760
89793
|
const rD = (!bConstrained || bAlternateMetrics) ? estResponsiveDistricts(VfArray) : undefined;
|
|
89761
89794
|
const rDf = bAlternateMetrics ? estResponsiveDistrictsShare(rD, N) : undefined;
|
|
89762
89795
|
const gamma = (bAlternateMetrics && littleR) ? calcGamma(Vf, estSf, littleR) : undefined;
|
|
89763
89796
|
const Cn = countCompetitiveDistricts(VfArray);
|
|
89764
|
-
|
|
89797
|
+
// NOTE - Cd by definition uses a *possibly* different (more narrow) probability
|
|
89798
|
+
// distribution than Rd.
|
|
89799
|
+
const cD = estCompetitiveDistricts(VfArray);
|
|
89765
89800
|
// const cD = bConstrained ? estCompetitiveDistricts(VfArray) : rD as number;
|
|
89766
89801
|
const cDf = estCompetitiveDistrictsShare(cD, N);
|
|
89767
|
-
const
|
|
89768
|
-
|
|
89769
|
-
const
|
|
89770
|
-
const
|
|
89802
|
+
const competitivenessScore = scoreCompetitiveness(cDf);
|
|
89803
|
+
// NOTE: Original version:
|
|
89804
|
+
// const Mrange = findMarginalDistricts(Vf, VfArray, N);
|
|
89805
|
+
// const Md = estMarginalCompetitiveDistricts(Mrange, VfArray);
|
|
89806
|
+
// const Mdf = estMarginalCompetitiveShare(Md, Mrange);
|
|
89807
|
+
// const competitivenessScore = scoreCompetitiveness(Mdf, cDf);
|
|
89771
89808
|
let biasScoring = {
|
|
89772
89809
|
bestS: bestS,
|
|
89773
89810
|
bestSf: bestSf,
|
|
@@ -89784,9 +89821,9 @@ function scorePartisan(Vf, VfArray, options) {
|
|
|
89784
89821
|
cSimple: Cn,
|
|
89785
89822
|
cD: cD,
|
|
89786
89823
|
cDf: cDf,
|
|
89787
|
-
mRange: Mrange,
|
|
89788
|
-
mD: Md,
|
|
89789
|
-
mDf: Mdf,
|
|
89824
|
+
// mRange: Mrange,
|
|
89825
|
+
// mD: Md,
|
|
89826
|
+
// mDf: Mdf,
|
|
89790
89827
|
score: competitivenessScore
|
|
89791
89828
|
};
|
|
89792
89829
|
if (bAlternateMetrics) {
|
|
@@ -89821,6 +89858,8 @@ function scorePartisan(Vf, VfArray, options) {
|
|
|
89821
89858
|
bias: biasScoring,
|
|
89822
89859
|
impact: impactScoring,
|
|
89823
89860
|
competitiveness: competitiveScoring,
|
|
89861
|
+
dSVpoints: dSVpoints,
|
|
89862
|
+
rSVpoints: rSVpoints,
|
|
89824
89863
|
score: acrossStatesPartisanScore,
|
|
89825
89864
|
score2: withinStatesPartisanScore,
|
|
89826
89865
|
details: {}
|
|
@@ -89902,32 +89941,59 @@ function isAntimajoritarian(Vf, Sf) {
|
|
|
89902
89941
|
return bDem || bRep;
|
|
89903
89942
|
}
|
|
89904
89943
|
exports.isAntimajoritarian = isAntimajoritarian;
|
|
89905
|
-
|
|
89906
|
-
|
|
89907
|
-
|
|
89908
|
-
|
|
89909
|
-
|
|
89944
|
+
// COMPETITIVENESS - Revised 06/23/2020
|
|
89945
|
+
// Normalize overall competitiveness - Raw values are in the range [0.0–1.0].
|
|
89946
|
+
// But the practical max is more like 2/3's, so unitize that range to [0.0–1.0].
|
|
89947
|
+
// Then scale the values to [0–100].
|
|
89948
|
+
function scoreCompetitiveness(Cdf) {
|
|
89949
|
+
const _normalizer = new normalize_1.Normalizer(Cdf);
|
|
89910
89950
|
let worst = C.overallCompetitivenessRange()[C.BEG];
|
|
89911
89951
|
let best = C.overallCompetitivenessRange()[C.END];
|
|
89912
|
-
|
|
89913
|
-
|
|
89914
|
-
|
|
89915
|
-
const
|
|
89916
|
-
// Normalize marginal competitiveness
|
|
89917
|
-
const _marginal = new normalize_1.Normalizer(rawMarginal);
|
|
89918
|
-
worst = C.marginalCompetitivenessRange()[C.BEG];
|
|
89919
|
-
best = C.marginalCompetitivenessRange()[C.END];
|
|
89920
|
-
_marginal.clip(worst, best);
|
|
89921
|
-
_marginal.unitize(worst, best);
|
|
89922
|
-
_marginal.rescale();
|
|
89923
|
-
const mcS = _marginal.normalizedNum;
|
|
89924
|
-
const mcW = C.marginalCompetitivenessWeight();
|
|
89925
|
-
const ocW = C.overallCompetitivenessWeight();
|
|
89926
|
-
// Then combine the results
|
|
89927
|
-
const score = ((mcW + ocW) > 0) ? Math.round(((mcW * mcS) + (ocW * ocS)) / (mcW + ocW)) : 0;
|
|
89952
|
+
_normalizer.clip(worst, best);
|
|
89953
|
+
_normalizer.unitize(worst, best);
|
|
89954
|
+
_normalizer.rescale();
|
|
89955
|
+
const score = _normalizer.normalizedNum;
|
|
89928
89956
|
return score;
|
|
89929
89957
|
}
|
|
89930
89958
|
exports.scoreCompetitiveness = scoreCompetitiveness;
|
|
89959
|
+
/* NOTE - Original version:
|
|
89960
|
+
export function scoreCompetitiveness(rawMarginal: number, rawOverall: number): number
|
|
89961
|
+
{
|
|
89962
|
+
// Normalize overall competitiveness - Raw values are in the range [0.0–1.0].
|
|
89963
|
+
// But the practical max is more like 2/3's, so unitize that range to [0.0–1.0].
|
|
89964
|
+
// Then scale the values to [0–100].
|
|
89965
|
+
const _overall = new Normalizer(rawOverall);
|
|
89966
|
+
|
|
89967
|
+
let worst = C.overallCompetitivenessRange()[C.BEG];
|
|
89968
|
+
let best = C.overallCompetitivenessRange()[C.END];
|
|
89969
|
+
|
|
89970
|
+
_overall.clip(worst, best);
|
|
89971
|
+
_overall.unitize(worst, best);
|
|
89972
|
+
_overall.rescale();
|
|
89973
|
+
|
|
89974
|
+
const ocS = _overall.normalizedNum as number;
|
|
89975
|
+
|
|
89976
|
+
|
|
89977
|
+
// Normalize marginal competitiveness
|
|
89978
|
+
const _marginal = new Normalizer(rawMarginal);
|
|
89979
|
+
|
|
89980
|
+
worst = C.marginalCompetitivenessRange()[C.BEG];
|
|
89981
|
+
best = C.marginalCompetitivenessRange()[C.END];
|
|
89982
|
+
|
|
89983
|
+
_marginal.clip(worst, best);
|
|
89984
|
+
_marginal.unitize(worst, best);
|
|
89985
|
+
_marginal.rescale();
|
|
89986
|
+
const mcS = _marginal.normalizedNum as number;
|
|
89987
|
+
|
|
89988
|
+
const mcW = C.marginalCompetitivenessWeight();
|
|
89989
|
+
const ocW = C.overallCompetitivenessWeight();
|
|
89990
|
+
|
|
89991
|
+
// Then combine the results
|
|
89992
|
+
const score = ((mcW + ocW) > 0) ? Math.round(((mcW * mcS) + (ocW * ocS)) / (mcW + ocW)) : 0;
|
|
89993
|
+
|
|
89994
|
+
return score;
|
|
89995
|
+
}
|
|
89996
|
+
*/
|
|
89931
89997
|
// CORE CAPABILITIES FROM JOHN NAGLE'S METHOD
|
|
89932
89998
|
const { erf } = __webpack_require__(/*! mathjs */ "./node_modules/mathjs/main/esm/index.js");
|
|
89933
89999
|
// console.log("erf(0.2) =", erf(0.2)); // returns 0.22270258921047847
|
|
@@ -90161,19 +90227,20 @@ function isCompetitive(v) {
|
|
|
90161
90227
|
return ((v >= C.competitiveRange()[C.BEG]) && (v <= C.competitiveRange()[C.END])) ? 1 : 0;
|
|
90162
90228
|
}
|
|
90163
90229
|
// cD - The estimated # of competitive districts
|
|
90164
|
-
function estCompetitiveDistricts(VfArray) {
|
|
90165
|
-
return U.trim(U.sumArray(VfArray.map(v => estDistrictCompetitiveness(v))));
|
|
90230
|
+
function estCompetitiveDistricts(VfArray, bCompress = false) {
|
|
90231
|
+
return U.trim(U.sumArray(VfArray.map(v => estDistrictCompetitiveness(v, bCompress))));
|
|
90166
90232
|
}
|
|
90167
90233
|
exports.estCompetitiveDistricts = estCompetitiveDistricts;
|
|
90234
|
+
// COMPETITIVENESS - Modified 06/22/2020
|
|
90168
90235
|
// Re-scale a Democratic vote share to the competitive range (e.g., 45–55%).
|
|
90169
|
-
function estDistrictCompetitiveness(Vf) {
|
|
90236
|
+
function estDistrictCompetitiveness(Vf, bCompress = false) {
|
|
90170
90237
|
const _normalizer = new normalize_1.Normalizer(Vf);
|
|
90171
90238
|
// The end points of a compressed probability distribution
|
|
90172
90239
|
// NOTE - These aren't the points where races start or stop being contested,
|
|
90173
90240
|
// just the end points of a distribution that yields the desired behavior
|
|
90174
90241
|
// in the typical competitive range [45-55%].
|
|
90175
|
-
const distBeg = C.competitiveDistribution()[C.BEG];
|
|
90176
|
-
const distEnd = C.competitiveDistribution()[C.END];
|
|
90242
|
+
const distBeg = bCompress ? C.competitiveDistribution()[C.BEG] : 0.0;
|
|
90243
|
+
const distEnd = bCompress ? C.competitiveDistribution()[C.END] : 1.0;
|
|
90177
90244
|
_normalizer.clip(distBeg, distEnd);
|
|
90178
90245
|
_normalizer.unitize(distBeg, distEnd);
|
|
90179
90246
|
const dC = estDistrictResponsiveness(_normalizer.wipNum);
|
|
@@ -90187,14 +90254,14 @@ function estCompetitiveDistrictsShare(cD, N) {
|
|
|
90187
90254
|
exports.estCompetitiveDistrictsShare = estCompetitiveDistrictsShare;
|
|
90188
90255
|
// Md - The estimated # of "marginal" districts in and around the likely FPTP
|
|
90189
90256
|
// seats & the best seat split that are competitive.
|
|
90190
|
-
function estMarginalCompetitiveDistricts(Mrange, VfArray) {
|
|
90257
|
+
function estMarginalCompetitiveDistricts(Mrange, VfArray, bCompress = false) {
|
|
90191
90258
|
const minId = Mrange[C.BEG];
|
|
90192
90259
|
const maxId = Mrange[C.END];
|
|
90193
90260
|
// Sort the array values, and subset it to those districts
|
|
90194
90261
|
// NOTE - I'm *not* keeping track of the district indexes right now
|
|
90195
90262
|
let subsetVfArray = U.deepCopy(VfArray).sort().slice(minId - 1, maxId);
|
|
90196
90263
|
// Est. competitive districts on that array
|
|
90197
|
-
const Md = U.sumArray(subsetVfArray.map((v) => estDistrictCompetitiveness(v)));
|
|
90264
|
+
const Md = U.sumArray(subsetVfArray.map((v) => estDistrictCompetitiveness(v, bCompress)));
|
|
90198
90265
|
return U.trim(Md);
|
|
90199
90266
|
}
|
|
90200
90267
|
exports.estMarginalCompetitiveDistricts = estMarginalCompetitiveDistricts;
|
|
@@ -90292,9 +90359,9 @@ function estVotesBias(inferredSVpoints, nDistricts) {
|
|
|
90292
90359
|
}
|
|
90293
90360
|
exports.estVotesBias = estVotesBias;
|
|
90294
90361
|
// GEOMETRIC SEATS BIAS (@ V = statewide vote share)
|
|
90295
|
-
function estGeometricSeatsBias(Vf,
|
|
90362
|
+
function estGeometricSeatsBias(Vf, dSVpoints, rSVpoints) {
|
|
90296
90363
|
let BsGf = undefined;
|
|
90297
|
-
const bgsSVpoints = inferGeometricSeatsBiasPoints(
|
|
90364
|
+
const bgsSVpoints = inferGeometricSeatsBiasPoints(dSVpoints, rSVpoints);
|
|
90298
90365
|
// Interpolate the seat fraction @ Vf
|
|
90299
90366
|
const lowerPt = findBracketingLowerVf(Vf, bgsSVpoints);
|
|
90300
90367
|
const upperPt = findBracketingUpperVf(Vf, bgsSVpoints);
|
|
@@ -90307,14 +90374,13 @@ function estGeometricSeatsBias(Vf, inferredSVpoints) {
|
|
|
90307
90374
|
return BsGf;
|
|
90308
90375
|
}
|
|
90309
90376
|
exports.estGeometricSeatsBias = estGeometricSeatsBias;
|
|
90310
|
-
function inferGeometricSeatsBiasPoints(
|
|
90311
|
-
const nPoints =
|
|
90312
|
-
const inverseSVpoints = invertSVPoints(inferredSVpoints);
|
|
90377
|
+
function inferGeometricSeatsBiasPoints(dSVpoints, rSVpoints) {
|
|
90378
|
+
const nPoints = dSVpoints.length;
|
|
90313
90379
|
let bgsSVpoints = [];
|
|
90314
90380
|
for (let i = 0; i < nPoints; i++) {
|
|
90315
|
-
const Vf =
|
|
90316
|
-
const sD =
|
|
90317
|
-
const sR =
|
|
90381
|
+
const Vf = dSVpoints[i].v;
|
|
90382
|
+
const sD = dSVpoints[i].s;
|
|
90383
|
+
const sR = rSVpoints[i].s;
|
|
90318
90384
|
const BsGf = 0.5 * (sR - sD);
|
|
90319
90385
|
bgsSVpoints.push({ v: Vf, s: BsGf });
|
|
90320
90386
|
}
|
|
@@ -90473,11 +90539,10 @@ function calcLopsidedOutcomes(VfArray) {
|
|
|
90473
90539
|
}
|
|
90474
90540
|
exports.calcLopsidedOutcomes = calcLopsidedOutcomes;
|
|
90475
90541
|
// GLOBAL SYMMETRY - Fig. 17 in Section 5.1
|
|
90476
|
-
function calcGlobalSymmetry(
|
|
90477
|
-
const invertedSVpoints = invertSVPoints(inferredSVpoints);
|
|
90542
|
+
function calcGlobalSymmetry(dSVpoints, rSVpoints, S50V) {
|
|
90478
90543
|
let gSym = 0.0;
|
|
90479
|
-
for (let i in
|
|
90480
|
-
gSym += Math.abs(
|
|
90544
|
+
for (let i in dSVpoints) {
|
|
90545
|
+
gSym += Math.abs(dSVpoints[i].s - rSVpoints[i].s) / 2;
|
|
90481
90546
|
}
|
|
90482
90547
|
const sign = (S50V < 0) ? -1 : 1;
|
|
90483
90548
|
gSym *= sign;
|
|
@@ -90537,7 +90602,7 @@ function calcGamma(Vf, Sf, r) {
|
|
|
90537
90602
|
exports.calcGamma = calcGamma;
|
|
90538
90603
|
// HELPERS
|
|
90539
90604
|
function printPartisanScorecardHeader() {
|
|
90540
|
-
console.log('XX, Name, N, V%, ^S#, S#, B%, B$, UE#, I$, C#, Cd,
|
|
90605
|
+
console.log('XX, Name, N, V%, ^S#, S#, B%, B$, UE#, I$, C#, Cd, Cdf, C$, <P$');
|
|
90541
90606
|
}
|
|
90542
90607
|
exports.printPartisanScorecardHeader = printPartisanScorecardHeader;
|
|
90543
90608
|
function printPartisanScorecardRow(xx, name, N, Vf, s) {
|
|
@@ -90550,21 +90615,24 @@ function printPartisanScorecardRow(xx, name, N, Vf, s) {
|
|
|
90550
90615
|
s.bias.bias, // 7
|
|
90551
90616
|
s.bias.score, s.impact.unearnedS, // 9
|
|
90552
90617
|
s.impact.score, s.competitiveness.cSimple, // 11
|
|
90553
|
-
s.competitiveness.cD, s.competitiveness.
|
|
90618
|
+
s.competitiveness.cD, s.competitiveness.cDf,
|
|
90619
|
+
// s.competitiveness.mD,
|
|
90620
|
+
s.competitiveness.score, s.score // 15
|
|
90554
90621
|
);
|
|
90555
90622
|
}
|
|
90556
90623
|
exports.printPartisanScorecardRow = printPartisanScorecardRow;
|
|
90557
90624
|
// Generate partisan details (Table 1)
|
|
90558
90625
|
function printPartisanDetailsHeader() {
|
|
90559
|
-
console.log('XX, <V>, S(<V>), B%, BS_50, BV_50, Decl, GS, EG, Beta, PR, MM, TO, MM\', LO, Rd, R, r, MIR');
|
|
90626
|
+
console.log('XX, <V>, S(<V>), B%, BS_50, BV_50, Decl, GS, EG, Beta, PR, MM, TO, MM\', LO, Rd, R, r, MIR, Cd, Cdf');
|
|
90560
90627
|
}
|
|
90561
90628
|
exports.printPartisanDetailsHeader = printPartisanDetailsHeader;
|
|
90562
90629
|
function printPartisanDetailsRow(xx, name, N, Vf, s) {
|
|
90563
|
-
console.log('%s, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f', xx, Vf, s.bias.estSf, s.bias.bias, // Simple bias
|
|
90630
|
+
console.log('%s, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f', xx, Vf, s.bias.estSf, s.bias.bias, // Simple bias
|
|
90564
90631
|
s.bias.bS50, s.bias.bV50, s.bias.decl, s.bias.gSym, s.bias.eG, s.bias.bSV, // Beta
|
|
90565
90632
|
s.bias.prop, // PR
|
|
90566
|
-
s.bias.mMs, s.bias.tOf, s.bias.mMd, s.bias.lO, s.competitiveness.rD, s.competitiveness.bigR, s.competitiveness.littleR, s.competitiveness.mIR // Zeta
|
|
90567
|
-
|
|
90633
|
+
s.bias.mMs, s.bias.tOf, s.bias.mMd, s.bias.lO, s.competitiveness.rD, s.competitiveness.bigR, s.competitiveness.littleR, s.competitiveness.mIR, // Zeta
|
|
90634
|
+
s.competitiveness.cD, // COMPETITIVENESS - Temporary to confirm new calc
|
|
90635
|
+
s.competitiveness.cDf);
|
|
90568
90636
|
}
|
|
90569
90637
|
exports.printPartisanDetailsRow = printPartisanDetailsRow;
|
|
90570
90638
|
|
|
@@ -90694,11 +90762,11 @@ function mixinMinorityBonus(score, minorityBonus) {
|
|
|
90694
90762
|
}
|
|
90695
90763
|
exports.mixinMinorityBonus = mixinMinorityBonus;
|
|
90696
90764
|
function printScorecardHeader() {
|
|
90697
|
-
console.log('XX, Name, N, V%, ^S#, S#, B%, B$, UE#, I$, C#, Cd,
|
|
90765
|
+
console.log('XX, Name, N, V%, ^S#, S#, B%, B$, UE#, I$, C#, Cd, Cdf, C$, >P$, Rc, Rc$, Pc, Pc$, G$, Cs, Cs$, Ds, Ds$, S$, Eq, Eq$, T$, Od, Md, M$, $$$');
|
|
90698
90766
|
}
|
|
90699
90767
|
exports.printScorecardHeader = printScorecardHeader;
|
|
90700
90768
|
function printScorecardRow(xx, name, N, Vf, s) {
|
|
90701
|
-
console.log('%s, %s, %i, %f, %i, %f, %f, %i, %f, %i, %i, %f, %f, %i, %i, %f, %i, %f, %i, %i, %f, %i, %f, %i, %i, %f, %i, %i, %f, %i, %i', xx, // 1
|
|
90769
|
+
console.log('%s, %s, %i, %f, %i, %f, %f, %i, %f, %i, %i, %f, %f, %i, %i, %f, %i, %f, %i, %i, %f, %i, %f, %i, %i, %f, %i, %i, %f, %f, %i, %i', xx, // 1
|
|
90702
90770
|
name, // 2
|
|
90703
90771
|
N, // 3
|
|
90704
90772
|
Vf, // 4
|
|
@@ -90707,8 +90775,10 @@ function printScorecardRow(xx, name, N, Vf, s) {
|
|
|
90707
90775
|
s.partisan.bias.bias, // 7
|
|
90708
90776
|
s.partisan.bias.score, s.partisan.impact.unearnedS, // 9
|
|
90709
90777
|
s.partisan.impact.score, s.partisan.competitiveness.cSimple, // 11
|
|
90710
|
-
s.partisan.competitiveness.cD, s.partisan.competitiveness.
|
|
90711
|
-
|
|
90778
|
+
s.partisan.competitiveness.cD, s.partisan.competitiveness.cDf,
|
|
90779
|
+
// s.partisan.competitiveness.mD,
|
|
90780
|
+
s.partisan.competitiveness.score, s.partisan.score2, // 15
|
|
90781
|
+
s.traditionalPrinciples.compactness.reock.raw, s.traditionalPrinciples.compactness.reock.normalized, s.traditionalPrinciples.compactness.polsby.raw, s.traditionalPrinciples.compactness.polsby.normalized, s.traditionalPrinciples.compactness.score, s.traditionalPrinciples.splitting.county.raw, s.traditionalPrinciples.splitting.county.normalized, s.traditionalPrinciples.splitting.district.raw, s.traditionalPrinciples.splitting.district.normalized, s.traditionalPrinciples.splitting.score, s.traditionalPrinciples.populationDeviation.raw, s.traditionalPrinciples.populationDeviation.normalized, s.traditionalPrinciples.score, s.minority.opportunityDistricts, s.minority.coalitionDistricts, s.minority.score, s.score);
|
|
90712
90782
|
}
|
|
90713
90783
|
exports.printScorecardRow = printScorecardRow;
|
|
90714
90784
|
|
|
@@ -102853,6 +102923,17 @@ class AnalyticsSession {
|
|
|
102853
102923
|
getPlanScorecard(bLog = false) {
|
|
102854
102924
|
return this._scorecard;
|
|
102855
102925
|
}
|
|
102926
|
+
getRatings(bLog = false) {
|
|
102927
|
+
const scorecard = this._scorecard;
|
|
102928
|
+
const r = {
|
|
102929
|
+
proportionality: scorecard.partisan.bias.score,
|
|
102930
|
+
competitiveness: scorecard.partisan.competitiveness.score,
|
|
102931
|
+
minorityRights: scorecard.minority.score,
|
|
102932
|
+
compactness: scorecard.traditionalPrinciples.compactness.score,
|
|
102933
|
+
splitting: scorecard.traditionalPrinciples.splitting.score
|
|
102934
|
+
};
|
|
102935
|
+
return r;
|
|
102936
|
+
}
|
|
102856
102937
|
// NOTE - This assumes that analyzePlan() has been run!
|
|
102857
102938
|
getRequirementsChecklist(bLog = false) {
|
|
102858
102939
|
return results_2.prepareRequirementsChecklist(this, bLog);
|