<jupyter_start><jupyter_text>Connecticut Redistricting Analysis: GerryChain EDA- Project Objective: Analyze final 2021 CT State House and State Senate maps relative to incumbent protection- Notebook Objective: Set-up seed plan selection, constraint functions, acceptance functions  Context >(Cod. Conn. Const. Art. III., Sec. 3, as amended.)(Senate, number, qualifications.) Sec. 3. The senate shall consist of not less than thirty and not more than fifty members, each of whom shall have attained the age of eighteen and be an elector residing in the senatorial district from which he is elected. *Each senatorial district shall be contiguous as to territory and shall elect no more than one senator.*">(Cod. Conn. Const. Art. III., Sec. 4, as amended.) (House of representatives, how constituted.)Sec. 4. The house of representatives shall consist of not less than one hundred twenty-five and not more than two hundred twenty-five members, each of whom shall have attained the age of eighteen years and be an elector residing in the assembly district from which he is elected. *Each assembly district shall be contiguous as to territory and shall elect no more than one representative. For the purpose of forming assembly districts no town shall be divided except for the purpose of forming assembly districts wholly within the town.*">(Cod. Conn. Const. Art. III., Sec. 5, as amended.)(Congressional and general assembly districts to be consistent with federal standards.)Sec. 5. The establishment of congressional districts and of districts in the general assembly shall be consistent with federal constitutional standards.[Source](https://www.cga.ct.gov/rr/tfs/20210401_2021%20Redistricting%20Project/laws.asp) Therefore, the following ensemble analysis integrates the following legal rules: - Districts shall be compact and contiguous- Towns will not be split- Population equality  Import and Clean DataTwo blocks are identified with overlaps and a single island is identified. The island was removed.<jupyter_code>import os
import sys
import random
from functools import partial

import geopandas as gpd
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
import pandas as pd
#import seaborn as sns

from gerrychain import (
    Election,
    Graph,
    MarkovChain,
    Partition,
    accept, 
    constraints
)

from gerrychain.proposals import recom, propose_random_flip
from gerrychain.metrics import efficiency_gap, mean_median, polsby_popper, wasted_votes
from gerrychain.updaters import cut_edges, county_splits, Tally
from gerrychain.tree import recursive_tree_part, bipartition_tree_random
ct_graph = Graph.from_json("./data/CT_dual_graph.json")
ct_df = gpd.read_file("./data/CT_analysis.zip")
#UserWarning: Found overlaps among the given polygons. Indices of overlaps: {(33614, 33615)}
#UserWarning: Found islands (degree-0 nodes). Indices of islands: {36463} 
#"Found islands (degree-0 nodes). Indices of islands: {}".format(islands)

ct_graph.remove_node(36463)<jupyter_output><empty_output><jupyter_text>Map the Dual Graph and Geopandas<jupyter_code>nx.draw(ct_graph,
        pos = {node:(ct_graph.nodes[node]["C_X"],
                     ct_graph.nodes[node]["C_Y"]) 
                     for node in ct_graph.nodes()},
        node_color=[ct_graph.nodes[node]["SENATE"] 
                    for node in ct_graph.nodes()],
        node_size=10,
        cmap='tab20')
nx.draw(ct_graph,
        pos = {node:(ct_graph.nodes[node]["C_X"],
                     ct_graph.nodes[node]["C_Y"]) 
                     for node in ct_graph.nodes()},
        node_color=[ct_graph.nodes[node]["HOUSE"] 
                    for node in ct_graph.nodes()],
        node_size=10,
        cmap='tab20')
ct_senate = ct_df.plot(column="SENATE", cmap="PuBuGn", edgecolor="face")
ct_senate.axis('off')
ct_house = ct_df.plot(column="HOUSE", cmap="PuBuGn", edgecolor="face")
ct_house.axis('off')<jupyter_output><empty_output><jupyter_text>Generic Updates and Shortcuts<jupyter_code>totpop = "VAP"
dist_num_cthouse = 151
dist_num_ctsen = 36

def num_splits(partition, df=ct_df):
    df["current"] = df.index.map(partition.assignment)
    return sum(df.groupby("town")["current"].nunique() > 1)

def num_incumbents(partition, df=ct_df):
    df["current"] = df.index.map(partition.assignment)
    df['current'] = df['current'].astype(str).replace('\.0', '', regex=True)
    df["incumbent_chamber"] = 0
    df.loc[(df['chamber'] == 'ct_house') & (df['INCUMBENT'] == 1), 
           'incumbent_chamber'] = 1
    incum_numby_dist = df.groupby("current")["incumbent_chamber"].sum().value_counts()
    incum_dist_sort = incum_numby_dist.sort_index()
    return incum_dist_sort

updater = {
    "population": Tally(totpop, alias="population"),
    "cut_edges": cut_edges,
    "town_splits": num_splits,
    "incumbent": num_incumbents
    
}

total_population = sum([ct_graph.nodes[n][totpop] for n in ct_graph.nodes])<jupyter_output><empty_output><jupyter_text>Seed Plan- Examine seed plans without constraints<jupyter_code>seeds_town=[]
seeds_incumbent=[]

#Running multiple seeds to note rules for potential starting plans
for n in range(100):
    plan_seed = recursive_tree_part(ct_graph, #graph object
                                    range(dist_num_cthouse), #how many districts
                                    total_population/dist_num_cthouse, #population target
                                    totpop, #population column, variable name
                                    .01, #epsilon value
                                    1)
    
    partition_seed = Partition(ct_graph,
                           plan_seed, 
                           updater)
    
    seeds_town.append(partition_seed["town_splits"])
    seeds_incumbent.append(partition_seed["incumbent"])
#Plot seed plans to see where incumbent values fall<jupyter_output><empty_output><jupyter_text>Constraint and Acceptance Functions<jupyter_code>#Compactness bound, contiguity, town acceptance function, population equality<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09379652605459057, 'fraction_numerical': 0.011910669975186104, 'mean_word_length': 3.829073482428115, 'pattern_counts': {'":': 4, '<': 21, '<?xml version=': 0, '>': 25, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '43022', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1883, 'n_tokens_neox': 1736, 'n_words': 545, 'path': '02_eda_modeling.ipynb', 'repo_name': 'ka-chang/RedistrictingCT', 'size': '6045'}