@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 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);
@@ -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
- 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);
89767
89800
  // const cD = bConstrained ? estCompetitiveDistricts(VfArray) : rD as number;
89768
89801
  const cDf = estCompetitiveDistrictsShare(cD, N);
89769
- const Mrange = findMarginalDistricts(Vf, VfArray, N);
89770
- const Md = estMarginalCompetitiveDistricts(Mrange, VfArray);
89771
- const Mdf = estMarginalCompetitiveShare(Md, Mrange);
89772
- 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);
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
- const okExtra = (0.5 - Vf) * (C.winnerBonus() - 1.0);
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
- if (Vf > 0.5)
89873
- return Math.min(bias - extra, 0);
89874
- else
89875
- return Math.max(bias - extra, 0);
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
- function scoreCompetitiveness(rawMarginal, rawOverall) {
89910
- // Normalize overall competitiveness - Raw values are in the range [0.0–1.0].
89911
- // But the practical max is more like 2/3's, so unitize that range to [0.0–1.0].
89912
- // Then scale the values to [0–100].
89913
- const _overall = new normalize_1.Normalizer(rawOverall);
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
- _overall.clip(worst, best);
89917
- _overall.unitize(worst, best);
89918
- _overall.rescale();
89919
- const ocS = _overall.normalizedNum;
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, Md, C$, <P$');
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.mD, s.competitiveness.score, s.score // 15
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, Md, C$, >P$, Rc, Rc$, Pc, Pc$, G$, Cs, Cs$, Ds, Ds$, S$, Eq, Eq$, T$, Od, M$, $$$');
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.mD, s.partisan.competitiveness.score, s.partisan.score2, // 15
90713
- 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);
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);