@dra2020/district-analytics 6.1.0 → 6.3.1
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 +179 -83
- 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);
|
|
@@ -89763,13 +89794,17 @@ function scorePartisan(Vf, VfArray, options) {
|
|
|
89763
89794
|
const rDf = bAlternateMetrics ? estResponsiveDistrictsShare(rD, N) : undefined;
|
|
89764
89795
|
const gamma = (bAlternateMetrics && littleR) ? calcGamma(Vf, estSf, littleR) : undefined;
|
|
89765
89796
|
const Cn = countCompetitiveDistricts(VfArray);
|
|
89766
|
-
|
|
89797
|
+
// NOTE - Cd by definition uses a *possibly* different (more narrow) probability
|
|
89798
|
+
// distribution than Rd.
|
|
89799
|
+
const cD = estCompetitiveDistricts(VfArray);
|
|
89767
89800
|
// const cD = bConstrained ? estCompetitiveDistricts(VfArray) : rD as number;
|
|
89768
89801
|
const cDf = estCompetitiveDistrictsShare(cD, N);
|
|
89769
|
-
const
|
|
89770
|
-
|
|
89771
|
-
const
|
|
89772
|
-
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);
|
|
89773
89808
|
let biasScoring = {
|
|
89774
89809
|
bestS: bestS,
|
|
89775
89810
|
bestSf: bestSf,
|
|
@@ -89786,9 +89821,9 @@ function scorePartisan(Vf, VfArray, options) {
|
|
|
89786
89821
|
cSimple: Cn,
|
|
89787
89822
|
cD: cD,
|
|
89788
89823
|
cDf: cDf,
|
|
89789
|
-
mRange: Mrange,
|
|
89790
|
-
mD: Md,
|
|
89791
|
-
mDf: Mdf,
|
|
89824
|
+
// mRange: Mrange,
|
|
89825
|
+
// mD: Md,
|
|
89826
|
+
// mDf: Mdf,
|
|
89792
89827
|
score: competitivenessScore
|
|
89793
89828
|
};
|
|
89794
89829
|
if (bAlternateMetrics) {
|
|
@@ -89841,7 +89876,10 @@ function weightPartisan(bS, iS, cS, context) {
|
|
|
89841
89876
|
}
|
|
89842
89877
|
exports.weightPartisan = weightPartisan;
|
|
89843
89878
|
function extraBonus(Vf) {
|
|
89844
|
-
|
|
89879
|
+
// PROPORTIONALITY - 06/24/2020 - Making the OK extra amount always positive.
|
|
89880
|
+
// const okExtra: number = (0.5 - Vf) * (C.winnerBonus() - 1.0);
|
|
89881
|
+
const over50Pct = (Vf > 0.5) ? (Vf - 0.5) : (0.5 - Vf);
|
|
89882
|
+
const okExtra = over50Pct * (C.winnerBonus() - 1.0);
|
|
89845
89883
|
return U.trim(okExtra);
|
|
89846
89884
|
}
|
|
89847
89885
|
exports.extraBonus = extraBonus;
|
|
@@ -89867,12 +89905,26 @@ function scorebias(rawBias, Vf, Sf) {
|
|
|
89867
89905
|
}
|
|
89868
89906
|
}
|
|
89869
89907
|
exports.scorebias = scorebias;
|
|
89908
|
+
// PROPORTIONALITY - 06/24/2020
|
|
89870
89909
|
// Adjust bias to account for a winner's bonus
|
|
89910
|
+
// * If the bias is in the *same* direction as the statewide vote %, then
|
|
89911
|
+
// discount the bias by the winner's bonus (extra).
|
|
89912
|
+
// * But if the bias and statewide vote % go in opposite directions, leave the
|
|
89913
|
+
// bias unadjusted.
|
|
89871
89914
|
function adjustBias(Vf, bias, extra) {
|
|
89872
|
-
|
|
89873
|
-
|
|
89874
|
-
|
|
89875
|
-
|
|
89915
|
+
let adjusted = bias;
|
|
89916
|
+
if ((Vf > 0.5) && (bias < 0)) {
|
|
89917
|
+
adjusted = Math.min(bias + extra, 0);
|
|
89918
|
+
}
|
|
89919
|
+
else if ((Vf < 0.5) && (bias > 0)) {
|
|
89920
|
+
adjusted = Math.max(bias - extra, 0);
|
|
89921
|
+
}
|
|
89922
|
+
return adjusted;
|
|
89923
|
+
// PROPORTIONALITY - 06/24/2020 - Original logic:
|
|
89924
|
+
// if (Vf > 0.5)
|
|
89925
|
+
// return Math.min(bias + extra, 0);
|
|
89926
|
+
// else
|
|
89927
|
+
// return Math.max(bias - extra, 0);
|
|
89876
89928
|
}
|
|
89877
89929
|
exports.adjustBias = adjustBias;
|
|
89878
89930
|
// Normalize unearned seats
|
|
@@ -89906,32 +89958,59 @@ function isAntimajoritarian(Vf, Sf) {
|
|
|
89906
89958
|
return bDem || bRep;
|
|
89907
89959
|
}
|
|
89908
89960
|
exports.isAntimajoritarian = isAntimajoritarian;
|
|
89909
|
-
|
|
89910
|
-
|
|
89911
|
-
|
|
89912
|
-
|
|
89913
|
-
|
|
89961
|
+
// COMPETITIVENESS - Revised 06/23/2020
|
|
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
|
+
function scoreCompetitiveness(Cdf) {
|
|
89966
|
+
const _normalizer = new normalize_1.Normalizer(Cdf);
|
|
89914
89967
|
let worst = C.overallCompetitivenessRange()[C.BEG];
|
|
89915
89968
|
let best = C.overallCompetitivenessRange()[C.END];
|
|
89916
|
-
|
|
89917
|
-
|
|
89918
|
-
|
|
89919
|
-
const
|
|
89920
|
-
// Normalize marginal competitiveness
|
|
89921
|
-
const _marginal = new normalize_1.Normalizer(rawMarginal);
|
|
89922
|
-
worst = C.marginalCompetitivenessRange()[C.BEG];
|
|
89923
|
-
best = C.marginalCompetitivenessRange()[C.END];
|
|
89924
|
-
_marginal.clip(worst, best);
|
|
89925
|
-
_marginal.unitize(worst, best);
|
|
89926
|
-
_marginal.rescale();
|
|
89927
|
-
const mcS = _marginal.normalizedNum;
|
|
89928
|
-
const mcW = C.marginalCompetitivenessWeight();
|
|
89929
|
-
const ocW = C.overallCompetitivenessWeight();
|
|
89930
|
-
// Then combine the results
|
|
89931
|
-
const score = ((mcW + ocW) > 0) ? Math.round(((mcW * mcS) + (ocW * ocS)) / (mcW + ocW)) : 0;
|
|
89969
|
+
_normalizer.clip(worst, best);
|
|
89970
|
+
_normalizer.unitize(worst, best);
|
|
89971
|
+
_normalizer.rescale();
|
|
89972
|
+
const score = _normalizer.normalizedNum;
|
|
89932
89973
|
return score;
|
|
89933
89974
|
}
|
|
89934
89975
|
exports.scoreCompetitiveness = scoreCompetitiveness;
|
|
89976
|
+
/* NOTE - Original version:
|
|
89977
|
+
export function scoreCompetitiveness(rawMarginal: number, rawOverall: number): number
|
|
89978
|
+
{
|
|
89979
|
+
// Normalize overall competitiveness - Raw values are in the range [0.0–1.0].
|
|
89980
|
+
// But the practical max is more like 2/3's, so unitize that range to [0.0–1.0].
|
|
89981
|
+
// Then scale the values to [0–100].
|
|
89982
|
+
const _overall = new Normalizer(rawOverall);
|
|
89983
|
+
|
|
89984
|
+
let worst = C.overallCompetitivenessRange()[C.BEG];
|
|
89985
|
+
let best = C.overallCompetitivenessRange()[C.END];
|
|
89986
|
+
|
|
89987
|
+
_overall.clip(worst, best);
|
|
89988
|
+
_overall.unitize(worst, best);
|
|
89989
|
+
_overall.rescale();
|
|
89990
|
+
|
|
89991
|
+
const ocS = _overall.normalizedNum as number;
|
|
89992
|
+
|
|
89993
|
+
|
|
89994
|
+
// Normalize marginal competitiveness
|
|
89995
|
+
const _marginal = new Normalizer(rawMarginal);
|
|
89996
|
+
|
|
89997
|
+
worst = C.marginalCompetitivenessRange()[C.BEG];
|
|
89998
|
+
best = C.marginalCompetitivenessRange()[C.END];
|
|
89999
|
+
|
|
90000
|
+
_marginal.clip(worst, best);
|
|
90001
|
+
_marginal.unitize(worst, best);
|
|
90002
|
+
_marginal.rescale();
|
|
90003
|
+
const mcS = _marginal.normalizedNum as number;
|
|
90004
|
+
|
|
90005
|
+
const mcW = C.marginalCompetitivenessWeight();
|
|
90006
|
+
const ocW = C.overallCompetitivenessWeight();
|
|
90007
|
+
|
|
90008
|
+
// Then combine the results
|
|
90009
|
+
const score = ((mcW + ocW) > 0) ? Math.round(((mcW * mcS) + (ocW * ocS)) / (mcW + ocW)) : 0;
|
|
90010
|
+
|
|
90011
|
+
return score;
|
|
90012
|
+
}
|
|
90013
|
+
*/
|
|
89935
90014
|
// CORE CAPABILITIES FROM JOHN NAGLE'S METHOD
|
|
89936
90015
|
const { erf } = __webpack_require__(/*! mathjs */ "./node_modules/mathjs/main/esm/index.js");
|
|
89937
90016
|
// console.log("erf(0.2) =", erf(0.2)); // returns 0.22270258921047847
|
|
@@ -90165,19 +90244,20 @@ function isCompetitive(v) {
|
|
|
90165
90244
|
return ((v >= C.competitiveRange()[C.BEG]) && (v <= C.competitiveRange()[C.END])) ? 1 : 0;
|
|
90166
90245
|
}
|
|
90167
90246
|
// cD - The estimated # of competitive districts
|
|
90168
|
-
function estCompetitiveDistricts(VfArray) {
|
|
90169
|
-
return U.trim(U.sumArray(VfArray.map(v => estDistrictCompetitiveness(v))));
|
|
90247
|
+
function estCompetitiveDistricts(VfArray, bCompress = false) {
|
|
90248
|
+
return U.trim(U.sumArray(VfArray.map(v => estDistrictCompetitiveness(v, bCompress))));
|
|
90170
90249
|
}
|
|
90171
90250
|
exports.estCompetitiveDistricts = estCompetitiveDistricts;
|
|
90251
|
+
// COMPETITIVENESS - Modified 06/22/2020
|
|
90172
90252
|
// Re-scale a Democratic vote share to the competitive range (e.g., 45–55%).
|
|
90173
|
-
function estDistrictCompetitiveness(Vf) {
|
|
90253
|
+
function estDistrictCompetitiveness(Vf, bCompress = false) {
|
|
90174
90254
|
const _normalizer = new normalize_1.Normalizer(Vf);
|
|
90175
90255
|
// The end points of a compressed probability distribution
|
|
90176
90256
|
// NOTE - These aren't the points where races start or stop being contested,
|
|
90177
90257
|
// just the end points of a distribution that yields the desired behavior
|
|
90178
90258
|
// in the typical competitive range [45-55%].
|
|
90179
|
-
const distBeg = C.competitiveDistribution()[C.BEG];
|
|
90180
|
-
const distEnd = C.competitiveDistribution()[C.END];
|
|
90259
|
+
const distBeg = bCompress ? C.competitiveDistribution()[C.BEG] : 0.0;
|
|
90260
|
+
const distEnd = bCompress ? C.competitiveDistribution()[C.END] : 1.0;
|
|
90181
90261
|
_normalizer.clip(distBeg, distEnd);
|
|
90182
90262
|
_normalizer.unitize(distBeg, distEnd);
|
|
90183
90263
|
const dC = estDistrictResponsiveness(_normalizer.wipNum);
|
|
@@ -90191,14 +90271,14 @@ function estCompetitiveDistrictsShare(cD, N) {
|
|
|
90191
90271
|
exports.estCompetitiveDistrictsShare = estCompetitiveDistrictsShare;
|
|
90192
90272
|
// Md - The estimated # of "marginal" districts in and around the likely FPTP
|
|
90193
90273
|
// seats & the best seat split that are competitive.
|
|
90194
|
-
function estMarginalCompetitiveDistricts(Mrange, VfArray) {
|
|
90274
|
+
function estMarginalCompetitiveDistricts(Mrange, VfArray, bCompress = false) {
|
|
90195
90275
|
const minId = Mrange[C.BEG];
|
|
90196
90276
|
const maxId = Mrange[C.END];
|
|
90197
90277
|
// Sort the array values, and subset it to those districts
|
|
90198
90278
|
// NOTE - I'm *not* keeping track of the district indexes right now
|
|
90199
90279
|
let subsetVfArray = U.deepCopy(VfArray).sort().slice(minId - 1, maxId);
|
|
90200
90280
|
// Est. competitive districts on that array
|
|
90201
|
-
const Md = U.sumArray(subsetVfArray.map((v) => estDistrictCompetitiveness(v)));
|
|
90281
|
+
const Md = U.sumArray(subsetVfArray.map((v) => estDistrictCompetitiveness(v, bCompress)));
|
|
90202
90282
|
return U.trim(Md);
|
|
90203
90283
|
}
|
|
90204
90284
|
exports.estMarginalCompetitiveDistricts = estMarginalCompetitiveDistricts;
|
|
@@ -90539,7 +90619,7 @@ function calcGamma(Vf, Sf, r) {
|
|
|
90539
90619
|
exports.calcGamma = calcGamma;
|
|
90540
90620
|
// HELPERS
|
|
90541
90621
|
function printPartisanScorecardHeader() {
|
|
90542
|
-
console.log('XX, Name, N, V%, ^S#, S#, B%, B$, UE#, I$, C#, Cd,
|
|
90622
|
+
console.log('XX, Name, N, V%, ^S#, S#, B%, B$, UE#, I$, C#, Cd, Cdf, C$, <P$');
|
|
90543
90623
|
}
|
|
90544
90624
|
exports.printPartisanScorecardHeader = printPartisanScorecardHeader;
|
|
90545
90625
|
function printPartisanScorecardRow(xx, name, N, Vf, s) {
|
|
@@ -90552,21 +90632,24 @@ function printPartisanScorecardRow(xx, name, N, Vf, s) {
|
|
|
90552
90632
|
s.bias.bias, // 7
|
|
90553
90633
|
s.bias.score, s.impact.unearnedS, // 9
|
|
90554
90634
|
s.impact.score, s.competitiveness.cSimple, // 11
|
|
90555
|
-
s.competitiveness.cD, s.competitiveness.
|
|
90635
|
+
s.competitiveness.cD, s.competitiveness.cDf,
|
|
90636
|
+
// s.competitiveness.mD,
|
|
90637
|
+
s.competitiveness.score, s.score // 15
|
|
90556
90638
|
);
|
|
90557
90639
|
}
|
|
90558
90640
|
exports.printPartisanScorecardRow = printPartisanScorecardRow;
|
|
90559
90641
|
// Generate partisan details (Table 1)
|
|
90560
90642
|
function printPartisanDetailsHeader() {
|
|
90561
|
-
console.log('XX, <V>, S(<V>), B%, BS_50, BV_50, Decl, GS, EG, Beta, PR, MM, TO, MM\', LO, Rd, R, r, MIR');
|
|
90643
|
+
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');
|
|
90562
90644
|
}
|
|
90563
90645
|
exports.printPartisanDetailsHeader = printPartisanDetailsHeader;
|
|
90564
90646
|
function printPartisanDetailsRow(xx, name, N, Vf, s) {
|
|
90565
|
-
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
|
|
90647
|
+
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
|
|
90566
90648
|
s.bias.bS50, s.bias.bV50, s.bias.decl, s.bias.gSym, s.bias.eG, s.bias.bSV, // Beta
|
|
90567
90649
|
s.bias.prop, // PR
|
|
90568
|
-
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
|
|
90569
|
-
|
|
90650
|
+
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
|
|
90651
|
+
s.competitiveness.cD, // COMPETITIVENESS - Temporary to confirm new calc
|
|
90652
|
+
s.competitiveness.cDf);
|
|
90570
90653
|
}
|
|
90571
90654
|
exports.printPartisanDetailsRow = printPartisanDetailsRow;
|
|
90572
90655
|
|
|
@@ -90696,11 +90779,11 @@ function mixinMinorityBonus(score, minorityBonus) {
|
|
|
90696
90779
|
}
|
|
90697
90780
|
exports.mixinMinorityBonus = mixinMinorityBonus;
|
|
90698
90781
|
function printScorecardHeader() {
|
|
90699
|
-
console.log('XX, Name, N, V%, ^S#, S#, B%, B$, UE#, I$, C#, Cd,
|
|
90782
|
+
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$, $$$');
|
|
90700
90783
|
}
|
|
90701
90784
|
exports.printScorecardHeader = printScorecardHeader;
|
|
90702
90785
|
function printScorecardRow(xx, name, N, Vf, s) {
|
|
90703
|
-
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
|
|
90786
|
+
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
|
|
90704
90787
|
name, // 2
|
|
90705
90788
|
N, // 3
|
|
90706
90789
|
Vf, // 4
|
|
@@ -90709,8 +90792,10 @@ function printScorecardRow(xx, name, N, Vf, s) {
|
|
|
90709
90792
|
s.partisan.bias.bias, // 7
|
|
90710
90793
|
s.partisan.bias.score, s.partisan.impact.unearnedS, // 9
|
|
90711
90794
|
s.partisan.impact.score, s.partisan.competitiveness.cSimple, // 11
|
|
90712
|
-
s.partisan.competitiveness.cD, s.partisan.competitiveness.
|
|
90713
|
-
|
|
90795
|
+
s.partisan.competitiveness.cD, s.partisan.competitiveness.cDf,
|
|
90796
|
+
// s.partisan.competitiveness.mD,
|
|
90797
|
+
s.partisan.competitiveness.score, s.partisan.score2, // 15
|
|
90798
|
+
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);
|
|
90714
90799
|
}
|
|
90715
90800
|
exports.printScorecardRow = printScorecardRow;
|
|
90716
90801
|
|
|
@@ -102855,6 +102940,17 @@ class AnalyticsSession {
|
|
|
102855
102940
|
getPlanScorecard(bLog = false) {
|
|
102856
102941
|
return this._scorecard;
|
|
102857
102942
|
}
|
|
102943
|
+
getRatings(bLog = false) {
|
|
102944
|
+
const scorecard = this._scorecard;
|
|
102945
|
+
const r = {
|
|
102946
|
+
proportionality: scorecard.partisan.bias.score,
|
|
102947
|
+
competitiveness: scorecard.partisan.competitiveness.score,
|
|
102948
|
+
minorityRights: scorecard.minority.score,
|
|
102949
|
+
compactness: scorecard.traditionalPrinciples.compactness.score,
|
|
102950
|
+
splitting: scorecard.traditionalPrinciples.splitting.score
|
|
102951
|
+
};
|
|
102952
|
+
return r;
|
|
102953
|
+
}
|
|
102858
102954
|
// NOTE - This assumes that analyzePlan() has been run!
|
|
102859
102955
|
getRequirementsChecklist(bLog = false) {
|
|
102860
102956
|
return results_2.prepareRequirementsChecklist(this, bLog);
|