@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 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\":{\"overall\":{\"range\":[0,0.67],\"weight\":100},\"marginal\":{\"range\":[0,0.85],\"weight\":0},\"range\":[0.45,0.55],\"distribution\":[0.25,0.75],\"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]}}");
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.range;
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.overall.range;
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
- const range = config_json_1.default.partisan.competitiveness.marginal.range;
89180
- return range;
89181
- }
89182
- exports.marginalCompetitivenessRange = marginalCompetitivenessRange;
89183
- function marginalCompetitivenessWeight(overridesJSON) {
89184
- const mcW = config_json_1.default.partisan.competitiveness.marginal.weight;
89185
- return mcW;
89186
- }
89187
- exports.marginalCompetitivenessWeight = marginalCompetitivenessWeight;
89188
- function overallCompetitivenessWeight(overridesJSON) {
89189
- const ocW = config_json_1.default.partisan.competitiveness.overall.weight;
89190
- return ocW;
89191
- }
89192
- exports.overallCompetitivenessWeight = overallCompetitivenessWeight;
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 /* Native */ + 1 - offset; // Ditto
89439
- const nBuckets = 2; // 37–50% and > 50%
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
- let bucketsByDemo = new Array(nDemos);
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
- bucketsByDemo[j] = [0, 0];
89445
- }
89446
- let opptyByDemo = U.initArray(nDemos, 0.0);
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 = U.sumArray(districtsByDemo.slice(1)); // Sum individual demos, skipping all minorities
89466
- const pCd = districtsByDemo[0]; // All minorities
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 inferredSVpoints = bAlternateMetrics ? inferSVpoints(Vf, VfArray, shift) : undefined;
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(inferredSVpoints, N) : undefined;
89777
+ const Bs50 = bAlternateMetrics ? estPartisanBias(dSVpoints, N) : undefined;
89745
89778
  const Bs50f = (!(Bs50 === undefined)) ? U.trim(Bs50 / N) : undefined;
89746
- const Bv50f = bAlternateMetrics ? estVotesBias(inferredSVpoints, N) : undefined;
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(inferredSVpoints, Bs50f) : undefined;
89782
+ const gSym = bAlternateMetrics ? calcGlobalSymmetry(dSVpoints, rSVpoints, Bs50f) : undefined;
89750
89783
  const EG = bAlternateMetrics ? calcEfficiencyGap(Vf, estSf) : undefined;
89751
- const BsGf = bAlternateMetrics ? estGeometricSeatsBias(Vf, inferredSVpoints) : undefined;
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, inferredSVpoints) : undefined;
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
- const cD = estCompetitiveDistricts(VfArray); // NOTE - Cd by definition uses a more narrow probability distribution vs. Rd
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 Mrange = findMarginalDistricts(Vf, VfArray, N);
89768
- const Md = estMarginalCompetitiveDistricts(Mrange, VfArray);
89769
- const Mdf = estMarginalCompetitiveShare(Md, Mrange);
89770
- const competitivenessScore = scoreCompetitiveness(Mdf, cDf);
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
- function scoreCompetitiveness(rawMarginal, rawOverall) {
89906
- // Normalize overall competitiveness - Raw values are in the range [0.0–1.0].
89907
- // But the practical max is more like 2/3's, so unitize that range to [0.0–1.0].
89908
- // Then scale the values to [0–100].
89909
- const _overall = new normalize_1.Normalizer(rawOverall);
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
- _overall.clip(worst, best);
89913
- _overall.unitize(worst, best);
89914
- _overall.rescale();
89915
- const ocS = _overall.normalizedNum;
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, inferredSVpoints) {
90362
+ function estGeometricSeatsBias(Vf, dSVpoints, rSVpoints) {
90296
90363
  let BsGf = undefined;
90297
- const bgsSVpoints = inferGeometricSeatsBiasPoints(inferredSVpoints);
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(inferredSVpoints) {
90311
- const nPoints = inferredSVpoints.length;
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 = inferredSVpoints[i].v;
90316
- const sD = inferredSVpoints[i].s;
90317
- const sR = inverseSVpoints[i].s;
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(inferredSVpoints, S50V) {
90477
- const invertedSVpoints = invertSVPoints(inferredSVpoints);
90542
+ function calcGlobalSymmetry(dSVpoints, rSVpoints, S50V) {
90478
90543
  let gSym = 0.0;
90479
- for (let i in inferredSVpoints) {
90480
- gSym += Math.abs(inferredSVpoints[i].s - invertedSVpoints[i].s) / 2;
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, Md, C$, <P$');
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.mD, s.competitiveness.score, s.score // 15
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, Md, C$, >P$, Rc, Rc$, Pc, Pc$, G$, Cs, Cs$, Ds, Ds$, S$, Eq, Eq$, T$, Od, M$, $$$');
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.mD, s.partisan.competitiveness.score, s.partisan.score2, // 15
90711
- 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.score, s.score);
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);