@dra2020/district-analytics 10.1.2 → 10.2.2

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.
@@ -177,8 +177,8 @@ class AnalyticsSession {
177
177
  }
178
178
  // 11-03-2020 - Added for racially polarized voting analysis
179
179
  // NOTE - This assumes that analyzePlan() has been run!
180
- analyzeRacialPolarization(id, bLog = false) {
181
- return minority_1.doAnalyzeRacialPolarization(this, id, bLog);
180
+ analyzeRacialPolarization(districtID, groups, bLog = false) {
181
+ return minority_1.doAnalyzeRacialPolarization(this, districtID, groups, bLog);
182
182
  }
183
183
  // NOTE - This assumes that analyzePlan() has been run!
184
184
  getDistrictStatistics(bLog = false) {
@@ -669,7 +669,7 @@ class Districts {
669
669
  return countyIndex;
670
670
  }
671
671
  // 11-03-2020 - Added for racially polarized voting analysis
672
- extractVotesByDemographic(districtID, bLog = false) {
672
+ extractVotesByDemographic(districtID, groups, bLog = false) {
673
673
  let whitePts = [];
674
674
  let minorityPts = [];
675
675
  let blackPts = [];
@@ -696,39 +696,75 @@ class Districts {
696
696
  // Calculate the Dem two-party vote
697
697
  const featureDem = outerThis._session.features.fieldForFeature(f, "ELECTION" /* ELECTION */, "D" /* DemVotes */);
698
698
  const featureRep = outerThis._session.features.fieldForFeature(f, "ELECTION" /* ELECTION */, "R" /* RepVotes */);
699
- const featureTot = outerThis._session.features.fieldForFeature(f, "ELECTION" /* ELECTION */, "Tot" /* TotalVotes */);
700
- const pctDem = featureDem / (featureDem + featureRep);
701
- // Calculate the VAP/CVAP percentages by demographic
702
- const totalVAP = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "Tot" /* TotalPop */);
703
- if (totalVAP > 0) {
704
- const whitePop = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "Wh" /* WhitePop */);
705
- const pctWhite = whitePop / totalVAP;
706
- const minorityPop = totalVAP - whitePop;
707
- const pctMinority = minorityPop / totalVAP;
708
- const pctBlack = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "BlC" /* BlackPop */) / totalVAP;
709
- const pctHispanic = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "His" /* HispanicPop */) / totalVAP;
710
- const pctPacific = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "PacC" /* PacificPop */) / totalVAP;
711
- const pctAsian = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "AsnC" /* AsianPop */) / totalVAP;
712
- const pctNative = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "NatC" /* NativePop */) / totalVAP;
713
- whitePts.push({ x: pctWhite, y: pctDem });
714
- minorityPts.push({ x: pctMinority, y: pctDem });
715
- blackPts.push({ x: pctBlack, y: pctDem });
716
- hispanicPts.push({ x: pctHispanic, y: pctDem });
717
- pacificPts.push({ x: pctPacific, y: pctDem });
718
- asianPts.push({ x: pctAsian, y: pctDem });
719
- nativePts.push({ x: pctNative, y: pctDem });
699
+ // const featureTot = outerThis._session.features.fieldForFeature(f, T.Dataset.ELECTION, T.FeatureField.TotalVotes);
700
+ if ((featureDem + featureRep) > 0) {
701
+ const pctDem = featureDem / (featureDem + featureRep);
702
+ // Calculate the VAP/CVAP percentages by demographic
703
+ const totalVAP = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "Tot" /* TotalPop */);
704
+ if (totalVAP > 0) {
705
+ // Gather all points, for debugging purposes ...
706
+ const whitePop = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "Wh" /* WhitePop */);
707
+ const pctWhite = whitePop / totalVAP;
708
+ const minorityPop = totalVAP - whitePop;
709
+ const pctMinority = minorityPop / totalVAP;
710
+ const pctBlack = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "BlC" /* BlackPop */) / totalVAP;
711
+ const pctHispanic = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "His" /* HispanicPop */) / totalVAP;
712
+ const pctPacific = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "PacC" /* PacificPop */) / totalVAP;
713
+ const pctAsian = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "AsnC" /* AsianPop */) / totalVAP;
714
+ const pctNative = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "NatC" /* NativePop */) / totalVAP;
715
+ let bGoodPoints = true;
716
+ // bGoodPoints = (Number.isNaN(pctDem)) ? false : true;
717
+ // bGoodPoints = (Number.isNaN(pctWhite)) ? false : true;
718
+ // bGoodPoints = (Number.isNaN(pctMinority)) ? false : true;
719
+ // bGoodPoints = (Number.isNaN(pctBlack)) ? false : true;
720
+ // bGoodPoints = (Number.isNaN(pctHispanic)) ? false : true;
721
+ // bGoodPoints = (Number.isNaN(pctPacific)) ? false : true;
722
+ // bGoodPoints = (Number.isNaN(pctAsian)) ? false : true;
723
+ // bGoodPoints = (Number.isNaN(pctNative)) ? false : true;
724
+ if (bGoodPoints) {
725
+ whitePts.push({ x: pctWhite, y: pctDem });
726
+ minorityPts.push({ x: pctMinority, y: pctDem });
727
+ blackPts.push({ x: pctBlack, y: pctDem });
728
+ hispanicPts.push({ x: pctHispanic, y: pctDem });
729
+ pacificPts.push({ x: pctPacific, y: pctDem });
730
+ asianPts.push({ x: pctAsian, y: pctDem });
731
+ nativePts.push({ x: pctNative, y: pctDem });
732
+ }
733
+ else {
734
+ if (bLog) {
735
+ console.log("NaN value for feature ", featureID);
736
+ console.log("pctDem: ", pctDem);
737
+ console.log("pctWhite: ", pctWhite);
738
+ console.log("pctMinority: ", pctMinority);
739
+ console.log("pctBlack: ", pctBlack);
740
+ console.log("pctHispanic: ", pctHispanic);
741
+ console.log("pctPacific: ", pctPacific);
742
+ console.log("pctAsian: ", pctAsian);
743
+ console.log("pctNative: ", pctNative);
744
+ }
745
+ }
746
+ }
747
+ else {
748
+ if (bLog)
749
+ console.log("Total VAP not > 0.");
750
+ }
751
+ }
752
+ else {
753
+ if (bLog)
754
+ console.log("D + R not > 0.");
720
755
  }
721
756
  }
722
757
  }
723
758
  });
759
+ // ... but only keep points for the demographics that are going to be analyzed
724
760
  return {
725
761
  white: whitePts,
726
- minority: minorityPts,
727
- black: blackPts,
728
- hispanic: hispanicPts,
729
- pacific: pacificPts,
730
- asian: asianPts,
731
- native: nativePts
762
+ minority: groups.minority ? minorityPts : [],
763
+ black: groups.black ? blackPts : [],
764
+ hispanic: groups.hispanic ? hispanicPts : [],
765
+ pacific: groups.pacific ? pacificPts : [],
766
+ asian: groups.asian ? asianPts : [],
767
+ native: groups.native ? nativePts : []
732
768
  };
733
769
  }
734
770
  // If a district is empty
@@ -1407,7 +1443,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
1407
1443
  };
1408
1444
  Object.defineProperty(exports, "__esModule", { value: true });
1409
1445
  const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
1410
- const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
1411
1446
  const majority_minority_json_1 = __importDefault(__webpack_require__(/*! ../static/majority-minority.json */ "./static/majority-minority.json"));
1412
1447
  const vra5_preclearance_json_1 = __importDefault(__webpack_require__(/*! ../static/vra5-preclearance.json */ "./static/vra5-preclearance.json"));
1413
1448
  // TODO - 2020: Update/revise this, when the update comes out in September:
@@ -1431,33 +1466,36 @@ function getVRASection5(s) {
1431
1466
  exports.getVRASection5 = getVRASection5;
1432
1467
  // 11-03-2020 - Added for racially polarized voting analysis
1433
1468
  // Analyze the degree of racially polarized voting for a district
1434
- function doAnalyzeRacialPolarization(s, districtID, bLog = false) {
1435
- const points = s.districts.extractVotesByDemographic(districtID, bLog);
1436
- if (points === undefined)
1469
+ function doAnalyzeRacialPolarization(s, districtID, groups, bLog = false) {
1470
+ // Either take take the minorities to analyze in as selections -or- infer them
1471
+ // from the % VAP/CVAP for the district (not the state).
1472
+ if (groups == undefined) {
1473
+ const RPV_THRESHOLD = 0.35; // Using ~ the Bethune–Hill threshold
1474
+ groups = {
1475
+ white: true,
1476
+ minority: false,
1477
+ black: (s.districts.table.blackPct[districtID] > RPV_THRESHOLD) ? true : false,
1478
+ hispanic: (s.districts.table.hispanicPct[districtID] > RPV_THRESHOLD) ? true : false,
1479
+ pacific: (s.districts.table.pacificPct[districtID] > RPV_THRESHOLD) ? true : false,
1480
+ asian: (s.districts.table.asianPct[districtID] > RPV_THRESHOLD) ? true : false,
1481
+ native: (s.districts.table.nativePct[districtID] > RPV_THRESHOLD) ? true : false
1482
+ };
1483
+ }
1484
+ if (!(groups.black || groups.hispanic || groups.pacific || groups.asian || groups.native))
1437
1485
  return undefined;
1438
- const nDistricts = s.state.nDistricts;
1439
- const statewide = score_1.getStatewideDemographics(s, bLog);
1440
- // Which minority groups are big enough that proportional representation would give them one or more seats?
1441
- const bDoBlack = (calcProportionalDistricts(statewide.black, nDistricts) > 0) ? true : false;
1442
- const bDoHispanic = (calcProportionalDistricts(statewide.hispanic, nDistricts) > 0) ? true : false;
1443
- const bDoPacific = (calcProportionalDistricts(statewide.pacific, nDistricts) > 0) ? true : false;
1444
- const bDoAsian = (calcProportionalDistricts(statewide.asian, nDistricts) > 0) ? true : false;
1445
- const bDoNative = (calcProportionalDistricts(statewide.native, nDistricts) > 0) ? true : false;
1446
- if (!(bDoBlack || bDoHispanic || bDoPacific || bDoAsian || bDoNative))
1486
+ const points = s.districts.extractVotesByDemographic(districtID, groups, bLog);
1487
+ if (points === undefined)
1447
1488
  return undefined;
1448
1489
  // At this point, at least one single-race/ethnicity minority demographic needs
1449
- // to be analyzed, so white & all minorities combined will need to be as well.
1450
- //
1451
- // const bDoWhite: boolean = (calcProportionalDistricts(statewide.white, nDistricts) > 0) ? true : false;
1452
- // const bDoMinority: boolean = (calcProportionalDistricts(statewide.minority, nDistricts) > 0) ? true : false;
1490
+ // to be analyzed along with white.
1453
1491
  const result = {
1454
- white: calcMaxVoteShare(points.white),
1455
- minority: calcMaxVoteShare(points.minority),
1456
- black: bDoBlack ? calcMaxVoteShare(points.black) : undefined,
1457
- hispanic: bDoHispanic ? calcMaxVoteShare(points.hispanic) : undefined,
1458
- pacific: bDoPacific ? calcMaxVoteShare(points.pacific) : undefined,
1459
- asian: bDoAsian ? calcMaxVoteShare(points.asian) : undefined,
1460
- native: bDoNative ? calcMaxVoteShare(points.native) : undefined
1492
+ white: characterizeDemographicVoting(points.white),
1493
+ minority: groups.minority ? characterizeDemographicVoting(points.minority) : undefined,
1494
+ black: groups.black ? characterizeDemographicVoting(points.black) : undefined,
1495
+ hispanic: groups.hispanic ? characterizeDemographicVoting(points.hispanic) : undefined,
1496
+ pacific: groups.pacific ? characterizeDemographicVoting(points.pacific) : undefined,
1497
+ asian: groups.asian ? characterizeDemographicVoting(points.asian) : undefined,
1498
+ native: groups.native ? characterizeDemographicVoting(points.native) : undefined
1461
1499
  };
1462
1500
  return result;
1463
1501
  }
@@ -1492,11 +1530,17 @@ function linearRegression(points) {
1492
1530
  result['r2'] = Math.pow((n * sum_xy - sum_x * sum_y) / Math.sqrt((n * sum_xx - sum_x * sum_x) * (n * sum_yy - sum_y * sum_y)), 2);
1493
1531
  return result;
1494
1532
  }
1495
- function calcMaxVoteShare(points) {
1533
+ function characterizeDemographicVoting(points) {
1496
1534
  const { m, b, r2 } = linearRegression(points);
1497
1535
  const maxVAPPct = 1.0; // 100%
1498
1536
  const maxDemPct = Math.max(0.0, Math.min(1.0, (m * maxVAPPct) + b));
1499
- return U.trim(maxDemPct);
1537
+ const result = {
1538
+ m: U.trim(m),
1539
+ b: U.trim(b),
1540
+ r2: U.trim(r2),
1541
+ f1: U.trim(maxDemPct)
1542
+ };
1543
+ return result;
1500
1544
  }
1501
1545
 
1502
1546