ahnaconda commited on
Commit
e8afe79
0 Parent(s):

Duplicate from ahuang11/name-chronicles

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tflite filter=lfs diff=lfs merge=lfs -text
29
+ *.tgz filter=lfs diff=lfs merge=lfs -text
30
+ *.wasm filter=lfs diff=lfs merge=lfs -text
31
+ *.xz filter=lfs diff=lfs merge=lfs -text
32
+ *.zip filter=lfs diff=lfs merge=lfs -text
33
+ *.zst filter=lfs diff=lfs merge=lfs -text
34
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
35
+ names.db filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9
2
+
3
+ WORKDIR /code
4
+
5
+ COPY ./requirements.txt /code/requirements.txt
6
+ RUN python3 -m pip install --no-cache-dir --upgrade pip
7
+ RUN python3 -m pip install --no-cache-dir --upgrade -r /code/requirements.txt
8
+
9
+ COPY . .
10
+
11
+ CMD ["panel", "serve", "/code/app.py", "--address", "0.0.0.0", "--port", "7860", "--allow-websocket-origin", "*"]
12
+
13
+ RUN mkdir /.cache
14
+ RUN chmod 777 /.cache
15
+ RUN mkdir .chroma
16
+ RUN chmod 777 .chroma
README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Panel Template
3
+ emoji: 📈
4
+ colorFrom: gray
5
+ colorTo: green
6
+ sdk: docker
7
+ pinned: false
8
+ duplicated_from: ahuang11/name-chronicles
9
+ license: bsd-3-clause
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,479 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+
3
+ import duckdb
4
+ import holoviews as hv
5
+ import pandas as pd
6
+ import panel as pn
7
+ from bokeh.models import HoverTool
8
+ from langchain.callbacks.base import BaseCallbackHandler
9
+ from langchain.chat_models import ChatOpenAI
10
+
11
+ pn.extension(sizing_mode="stretch_width", notifications=True)
12
+ hv.extension("bokeh")
13
+
14
+ INSTRUCTIONS = """
15
+ #### Name Chronicles lets you explore the history of names in the United States.
16
+ - Enter a name to add to plot.
17
+ - See stats by hovering a line.
18
+ - Click on a line to see the gender distribution.
19
+ - Get a random name based on selected criteria.
20
+ - Ask AI for some background info on a name.
21
+ - Have ideas? [Open an issue](https://github.com/ahuang11/name-chronicles/issues).
22
+ """
23
+
24
+ RANDOM_NAME_QUERY = """
25
+ SELECT name, count,
26
+ CASE
27
+ WHEN female_percent >= 0.2 AND female_percent <= 0.8 AND male_percent >= 0.2 AND male_percent <= 0.8 THEN 'unisex'
28
+ WHEN female_percent > 0.6 THEN 'female'
29
+ WHEN male_percent > 0.6 THEN 'male'
30
+ END AS gender
31
+ FROM (
32
+ SELECT
33
+ name,
34
+ MAX(male + female) AS count,
35
+ (SUM(female) / CAST(SUM(male + female) AS REAL)) AS female_percent,
36
+ (SUM(male) / CAST(SUM(male + female) AS REAL)) AS male_percent
37
+ FROM names
38
+ WHERE name LIKE ?
39
+ GROUP BY name
40
+ )
41
+ WHERE count >= ? AND count <= ?
42
+ AND gender = ?
43
+ ORDER BY RANDOM()
44
+ LIMIT 100
45
+ """
46
+
47
+ TOP_NAMES_WILDCARD_QUERY = """
48
+ SELECT name, SUM(male + female) as count
49
+ FROM names
50
+ WHERE lower(name) LIKE ?
51
+ GROUP BY name
52
+ ORDER BY count DESC
53
+ LIMIT 10
54
+ """
55
+
56
+ TOP_NAMES_SELECT_QUERY = """
57
+ SELECT name, SUM(male + female) as count
58
+ FROM names
59
+ WHERE lower(name) = ?
60
+ GROUP BY name
61
+ ORDER BY count DESC
62
+ """
63
+
64
+ DATA_QUERY = """
65
+ SELECT name, year, male, female, SUM(male + female) AS count
66
+ FROM names
67
+ WHERE name in ({placeholders})
68
+ GROUP BY name, year, male, female
69
+ ORDER BY name, year
70
+ """
71
+
72
+
73
+ class StreamHandler(BaseCallbackHandler):
74
+ def __init__(self, container, initial_text="", target_attr="value"):
75
+ self.container = container
76
+ self.text = initial_text
77
+ self.target_attr = target_attr
78
+
79
+ def on_llm_new_token(self, token: str, **kwargs) -> None:
80
+ self.text += token
81
+ setattr(self.container, self.target_attr, self.text)
82
+
83
+
84
+ class NameChronicles:
85
+ def __init__(self):
86
+ super().__init__()
87
+ self.db_path = Path("data/names.db")
88
+
89
+ # Main
90
+ self.holoviews_pane = pn.pane.HoloViews(
91
+ min_height=675, sizing_mode="stretch_both"
92
+ )
93
+ self.selection = hv.streams.Selection1D()
94
+
95
+ # Sidebar
96
+
97
+ # Name Widgets
98
+ self.names_input = pn.widgets.TextInput(name="Name Input", placeholder="Andrew")
99
+ self.names_input.param.watch(self._add_name, "value")
100
+
101
+ self.names_choice = pn.widgets.MultiChoice(
102
+ name="Selected Names",
103
+ options=["Andrew"],
104
+ solid=False,
105
+ )
106
+ self.names_choice.param.watch(self._update_plot, "value")
107
+
108
+ # Reset Widgets
109
+ self.clear_button = pn.widgets.Button(
110
+ name="Clear Names", button_style="outline", button_type="primary"
111
+ )
112
+ self.clear_button.on_click(
113
+ lambda event: setattr(self.names_choice, "value", [])
114
+ )
115
+ self.refresh_button = pn.widgets.Button(
116
+ name="Refresh Plot", button_style="outline", button_type="primary"
117
+ )
118
+ self.refresh_button.on_click(self._refresh_plot)
119
+
120
+ # Randomize Widgets
121
+ self.name_pattern = pn.widgets.TextInput(
122
+ name="Name Pattern", placeholder="*na*"
123
+ )
124
+ self.count_range = pn.widgets.IntRangeSlider(
125
+ name="Peak Count Range",
126
+ value=(10000, 50000),
127
+ start=0,
128
+ end=100000,
129
+ step=1000,
130
+ margin=(5, 20),
131
+ )
132
+ self.gender_select = pn.widgets.RadioButtonGroup(
133
+ name="Gender",
134
+ options=["Female", "Unisex", "Male"],
135
+ button_style="outline",
136
+ button_type="primary",
137
+ )
138
+ randomize_name = pn.widgets.Button(
139
+ name="Get Name", button_style="outline", button_type="primary"
140
+ )
141
+ randomize_name.param.watch(self._randomize_name, "clicks")
142
+ self.randomize_pane = pn.Card(
143
+ self.name_pattern,
144
+ self.count_range,
145
+ self.gender_select,
146
+ randomize_name,
147
+ title="Get Random Name",
148
+ collapsed=True,
149
+ )
150
+
151
+ # AI Widgets
152
+ self.ai_key = pn.widgets.PasswordInput(
153
+ name="OpenAI Key",
154
+ placeholder="",
155
+ )
156
+ self.ai_prompt = pn.widgets.TextInput(
157
+ name="AI Prompt",
158
+ value="Share a little history about the name:",
159
+ )
160
+ ai_button = pn.widgets.Button(
161
+ name="Get Response",
162
+ button_style="outline",
163
+ button_type="primary",
164
+ )
165
+ ai_button.on_click(self._prompt_ai)
166
+ self.ai_response = pn.widgets.TextAreaInput(
167
+ placeholder="",
168
+ disabled=True,
169
+ height=350,
170
+ )
171
+ self.ai_pane = pn.Card(
172
+ self.ai_key,
173
+ self.ai_prompt,
174
+ ai_button,
175
+ self.ai_response,
176
+ collapsed=True,
177
+ title="Ask AI",
178
+ )
179
+
180
+ pn.state.onload(self._initialize_database)
181
+
182
+ # Database Methods
183
+
184
+ def _initialize_database(self):
185
+ """
186
+ Initialize database with data from the Social Security Administration.
187
+ """
188
+ self.conn = duckdb.connect(":memory:")
189
+ df = pd.concat(
190
+ [
191
+ pd.read_csv(
192
+ path,
193
+ header=None,
194
+ names=["state", "gender", "year", "name", "count"],
195
+ )
196
+ for path in Path("data").glob("*.TXT")
197
+ ]
198
+ )
199
+ df_processed = (
200
+ df.groupby(["gender", "year", "name"], as_index=False)[["count"]]
201
+ .sum()
202
+ .pivot(index=["name", "year"], columns="gender", values="count")
203
+ .reset_index()
204
+ .rename(columns={"F": "female", "M": "male"})
205
+ .fillna(0)
206
+ )
207
+ self.conn.execute("DROP TABLE IF EXISTS names")
208
+ self.conn.execute("CREATE TABLE names AS SELECT * FROM df_processed")
209
+
210
+ if self.names_choice.value == []:
211
+ self.names_choice.value = ["Andrew"]
212
+ else:
213
+ self.names_choice.param.trigger("value")
214
+ self.main.objects = [self.holoviews_pane]
215
+
216
+ def _query_names(self, names):
217
+ """
218
+ Query the database for the given name.
219
+ """
220
+ dfs = []
221
+ for name in names:
222
+ if "*" in name or "%" in name:
223
+ name = name.replace("*", "%")
224
+ top_names_query = TOP_NAMES_WILDCARD_QUERY
225
+ else:
226
+ top_names_query = TOP_NAMES_SELECT_QUERY
227
+ top_names = (
228
+ self.conn.execute(top_names_query, [name.lower()])
229
+ .fetch_df()["name"]
230
+ .tolist()
231
+ )
232
+ if len(top_names) == 0:
233
+ pn.state.notifications.info(f"No names found matching {name!r}")
234
+ continue
235
+ data_query = DATA_QUERY.format(
236
+ placeholders=", ".join(["?"] * len(top_names))
237
+ )
238
+ df = self.conn.execute(data_query, top_names).fetch_df()
239
+ dfs.append(df)
240
+
241
+ if len(dfs) > 0:
242
+ self.df = pd.concat(dfs).drop_duplicates(
243
+ subset=["name", "year", "male", "female"]
244
+ )
245
+ else:
246
+ self.df = pd.DataFrame(columns=["name", "year", "male", "female"])
247
+
248
+ # Widget Methods
249
+
250
+ def _randomize_name(self, event):
251
+ name_pattern = self.name_pattern.value.lower()
252
+ if not name_pattern:
253
+ name_pattern = "%"
254
+ else:
255
+ name_pattern = name_pattern.replace("*", "%")
256
+ count_range = self.count_range.value
257
+ gender_select = self.gender_select.value.lower()
258
+ random_names = (
259
+ self.conn.execute(
260
+ RANDOM_NAME_QUERY, [name_pattern, *count_range, gender_select]
261
+ )
262
+ .fetch_df()["name"]
263
+ .tolist()
264
+ )
265
+ if random_names:
266
+ for i in range(len(random_names)):
267
+ random_name = random_names[i]
268
+ if random_name in self.names_choice.value:
269
+ continue
270
+ self.names_input.value = random_name
271
+ break
272
+ else:
273
+ pn.state.notifications.info(
274
+ "All names matching the criteria are already added!"
275
+ )
276
+ else:
277
+ pn.state.notifications.info("No names found matching the criteria!")
278
+
279
+ def _add_name(self, event):
280
+ name = event.new.strip().title()
281
+ self.names_input.value = ""
282
+ if not name:
283
+ return
284
+ elif name in self.names_choice.options and name in self.names_choice.value:
285
+ pn.state.notifications.info(f"{name!r} already added!")
286
+ return
287
+ elif len(self.names_choice.value) > 10:
288
+ pn.state.notifications.info(
289
+ "Maximum of 10 names allowed; please remove some first!"
290
+ )
291
+ return
292
+ value = self.names_choice.value.copy()
293
+ options = self.names_choice.options.copy()
294
+ if name not in options:
295
+ options.append(name)
296
+ if name not in value:
297
+ value.append(name)
298
+ self.names_choice.param.update(
299
+ options=options,
300
+ value=value,
301
+ )
302
+
303
+ def _prompt_ai(self, event):
304
+ if not self.ai_key.value:
305
+ pn.state.notifications.info("Please enter an API key!")
306
+ return
307
+
308
+ if not self.ai_prompt.value:
309
+ pn.state.notifications.info("Please enter a prompt!")
310
+ return
311
+
312
+ stream_handler = StreamHandler(self.ai_response)
313
+ chat = ChatOpenAI(
314
+ max_tokens=500,
315
+ openai_api_key=self.ai_key.value,
316
+ streaming=True,
317
+ callbacks=[stream_handler],
318
+ )
319
+ self.ai_response.loading = True
320
+ try:
321
+ if self.selection.index:
322
+ names = [self._name_indices[self.selection.index[0]]]
323
+ else:
324
+ names = self.names_choice.value[:3]
325
+ chat.predict(f"{self.ai_prompt.value} {names}")
326
+ finally:
327
+ self.ai_response.loading = False
328
+
329
+ # Plot Methods
330
+
331
+ def _click_plot(self, index):
332
+ gender_nd_overlay = hv.NdOverlay(kdims=["Gender"])
333
+ if not index:
334
+ return hv.NdOverlay(
335
+ {
336
+ "curve": self._curve_nd_overlay,
337
+ "scatter": self._scatter_nd_overlay,
338
+ "label": self._label_nd_overlay,
339
+ }
340
+ )
341
+
342
+ name = self._name_indices[index[0]]
343
+ df_name = self.df.loc[self.df["name"] == name].copy()
344
+ df_name["female"] += df_name["male"]
345
+ gender_nd_overlay["Male"] = hv.Area(
346
+ df_name, ["year"], ["male"], label="Male"
347
+ ).opts(alpha=0.3, color="#add8e6", line_alpha=0)
348
+ gender_nd_overlay["Female"] = hv.Area(
349
+ df_name, ["year"], ["male", "female"], label="Female"
350
+ ).opts(alpha=0.3, color="#ffb6c1", line_alpha=0)
351
+ return hv.NdOverlay(
352
+ {
353
+ "curve": self._curve_nd_overlay[[index[0]]],
354
+ "scatter": self._scatter_nd_overlay,
355
+ "label": self._label_nd_overlay[[index[0]]].opts(text_color="black"),
356
+ "gender": gender_nd_overlay,
357
+ },
358
+ kdims=["Gender"],
359
+ ).opts(legend_position="top_left")
360
+
361
+ @staticmethod
362
+ def _format_y(value):
363
+ return f"{value / 1000}k"
364
+
365
+ def _update_plot(self, event):
366
+ names = event.new
367
+ print(names)
368
+ self._query_names(names)
369
+
370
+ self._scatter_nd_overlay = hv.NdOverlay()
371
+ self._curve_nd_overlay = hv.NdOverlay(kdims=["Name"]).opts(
372
+ gridstyle={"xgrid_line_width": 0},
373
+ show_grid=True,
374
+ fontscale=1.28,
375
+ xlabel="Year",
376
+ ylabel="Count",
377
+ yformatter=self._format_y,
378
+ legend_limit=0,
379
+ padding=(0.2, 0.05),
380
+ title="Name Chronicles",
381
+ responsive=True,
382
+ )
383
+ self._label_nd_overlay = hv.NdOverlay(kdims=["Name"])
384
+ hover_tool = HoverTool(
385
+ tooltips=[("Name", "@name"), ("Year", "@year"), ("Count", "@count")],
386
+ )
387
+ self._name_indices = {}
388
+ scatter_cycle = hv.Cycle("Category10")
389
+ curve_cycle = hv.Cycle("Category10")
390
+ label_cycle = hv.Cycle("Category10")
391
+ for i, (name, df_name) in enumerate(self.df.groupby("name")):
392
+ df_name_total = df_name.groupby(
393
+ ["name", "year", "male", "female"], as_index=False
394
+ )["count"].sum()
395
+ df_name_total["male"] = df_name_total["male"] / df_name_total["count"]
396
+ df_name_total["female"] = df_name_total["female"] / df_name_total["count"]
397
+ df_name_peak = df_name.loc[[df_name["count"].idxmax()]]
398
+ df_name_peak[
399
+ "label"
400
+ ] = f'{df_name_peak["name"].item()} ({df_name_peak["year"].item()})'
401
+
402
+ hover_tool = HoverTool(
403
+ tooltips=[
404
+ ("Name", "@name"),
405
+ ("Year", "@year"),
406
+ ("Count", "@count{(0a)}"),
407
+ ("Male", "@male{(0%)}"),
408
+ ("Female", "@female{(0%)}"),
409
+ ],
410
+ )
411
+ self._scatter_nd_overlay[i] = hv.Scatter(
412
+ df_name_total, ["year"], ["count", "male", "female", "name"], label=name
413
+ ).opts(
414
+ color=scatter_cycle,
415
+ size=4,
416
+ alpha=0.15,
417
+ marker="y",
418
+ tools=["tap", hover_tool],
419
+ line_width=3,
420
+ show_legend=False,
421
+ )
422
+ self._curve_nd_overlay[i] = hv.Curve(
423
+ df_name_total, ["year"], ["count"], label=name
424
+ ).opts(
425
+ color=curve_cycle,
426
+ tools=["tap"],
427
+ line_width=3,
428
+ )
429
+ self._label_nd_overlay[i] = hv.Labels(
430
+ df_name_peak, ["year", "count"], ["label"], label=name
431
+ ).opts(
432
+ text_align="right",
433
+ text_baseline="bottom",
434
+ text_color=label_cycle,
435
+ )
436
+ self._name_indices[i] = name
437
+ self.selection.source = self._curve_nd_overlay
438
+ if len(self._name_indices) == 1:
439
+ self.selection.update(index=[0])
440
+ else:
441
+ self.selection.update(index=[])
442
+ self.dynamic_map = hv.DynamicMap(
443
+ self._click_plot, kdims=[], streams=[self.selection]
444
+ ).opts(responsive=True)
445
+ self._refresh_plot()
446
+
447
+ def _refresh_plot(self, event=None):
448
+ self.holoviews_pane.object = self.dynamic_map.clone()
449
+
450
+ def view(self):
451
+ reset_row = pn.Row(self.clear_button, self.refresh_button)
452
+ data_url = pn.pane.Markdown(
453
+ "<center>Data from the <a href='https://www.ssa.gov/oact/babynames/limits.html' "
454
+ "target='_blank'>U.S. Social Security Administration</a></center>",
455
+ align="end",
456
+ )
457
+ sidebar = pn.Column(
458
+ INSTRUCTIONS,
459
+ self.names_input,
460
+ self.names_choice,
461
+ reset_row,
462
+ pn.layout.Divider(),
463
+ self.randomize_pane,
464
+ self.ai_pane,
465
+ data_url,
466
+ )
467
+ self.main = pn.Column(
468
+ pn.widgets.StaticText(value="Loading, this may take a few seconds...", sizing_mode="stretch_both"),
469
+ )
470
+ template = pn.template.FastListTemplate(
471
+ sidebar=[sidebar],
472
+ main=[self.main],
473
+ title="Name Chronicles",
474
+ theme="dark",
475
+ )
476
+ return template
477
+
478
+
479
+ NameChronicles().view().servable()
data/STATE.AK.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.AL.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.AR.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.AZ.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.CA.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.CO.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.CT.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.DC.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.DE.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.FL.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.GA.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.HI.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.IA.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.ID.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.IL.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.IN.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.KS.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.KY.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.LA.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.MA.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.MD.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.ME.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.MI.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.MN.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.MO.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.MS.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.MT.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.NC.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.ND.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.NE.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.NH.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.NJ.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.NM.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.NV.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.NY.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.OH.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.OK.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.OR.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.PA.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.RI.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.SC.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.SD.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.TN.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.TX.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.UT.TXT ADDED
The diff for this file is too large to render. See raw diff
 
data/STATE.VA.TXT ADDED
The diff for this file is too large to render. See raw diff