nickil commited on
Commit
e1a22ca
β€’
1 Parent(s): 953eb88

Initial Commit

Browse files
Files changed (5) hide show
  1. app.py +26 -0
  2. config.json +251 -0
  3. data.py +68 -0
  4. network.py +61 -0
  5. requirements.txt +7 -0
app.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+
4
+ import gradio as gr
5
+ import pygwalker as pyg
6
+
7
+
8
+ from pygwalker.api.gradio import PYGWALKER_ROUTE, get_html_on_gradio
9
+
10
+
11
+ from data import df
12
+ from network import analysis
13
+
14
+
15
+ with gr.Blocks() as demo:
16
+ # with gr.Tab("πŸ“ About"):
17
+ # # gr.Markdown(ABOUT_TEXT)
18
+ with gr.Tab("πŸ“Š Dashboard"):
19
+ gr.Label("Visually explore witches family data")
20
+ gr.Markdown("You can use drag-and-drop operations to explore the data, start your analysis now!")
21
+ pyg_app = get_html_on_gradio(df, spec="./config.json")
22
+ gr.HTML(pyg_app)
23
+ with gr.Tab("πŸ‘ͺ Family Connection"):
24
+ gr.HTML(analysis)
25
+
26
+ demo.launch(app_kwargs={"routes": [PYGWALKER_ROUTE]}).queue()
config.json ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "config": {
4
+ "defaultAggregated": true,
5
+ "geoms": [
6
+ "poi"
7
+ ],
8
+ "coordSystem": "geographic",
9
+ "limit": -1
10
+ },
11
+ "encodings": {
12
+ "dimensions": [
13
+ {
14
+ "dragId": "gw_0oFN",
15
+ "fid": "GW_153M2HTVSG",
16
+ "name": "item",
17
+ "basename": "item",
18
+ "semanticType": "nominal",
19
+ "analyticType": "dimension"
20
+ },
21
+ {
22
+ "dragId": "gw_TfYx",
23
+ "fid": "GW_2X73E2RH6IPJOOK4G8G",
24
+ "name": "accusedurl",
25
+ "basename": "accusedurl",
26
+ "semanticType": "nominal",
27
+ "analyticType": "dimension"
28
+ },
29
+ {
30
+ "dragId": "gw_MK3G",
31
+ "fid": "GW_G0O5DCTUJ01U6ZK8G",
32
+ "name": "itemLabel",
33
+ "basename": "itemLabel",
34
+ "semanticType": "nominal",
35
+ "analyticType": "dimension"
36
+ },
37
+ {
38
+ "dragId": "gw_4Ud1",
39
+ "fid": "GW_6RT7I4HK7073D9RPOESM5CH80",
40
+ "name": "residence",
41
+ "basename": "residence",
42
+ "semanticType": "nominal",
43
+ "analyticType": "dimension"
44
+ },
45
+ {
46
+ "dragId": "gw_fW-1",
47
+ "fid": "GW_M27TBC9KBOWJRWX5WS34",
48
+ "name": "gender",
49
+ "basename": "gender",
50
+ "semanticType": "nominal",
51
+ "analyticType": "dimension"
52
+ },
53
+ {
54
+ "dragId": "gw_YRLn",
55
+ "fid": "GW_2ZE8EA85LWGIU48N1GW",
56
+ "name": "class",
57
+ "basename": "class",
58
+ "semanticType": "nominal",
59
+ "analyticType": "dimension"
60
+ },
61
+ {
62
+ "dragId": "gw_WKTM",
63
+ "fid": "GW_5498MX0A38QWHP82WQA4T7971KAQCP16WRJZZU8",
64
+ "name": "place_of_detention",
65
+ "basename": "place_of_detention",
66
+ "semanticType": "nominal",
67
+ "analyticType": "dimension"
68
+ },
69
+ {
70
+ "dragId": "gw_u9Xa",
71
+ "fid": "GW_R8BM6NZ296ISPYXOCTXCNQEAVA6142MCW7RHG7RLLBK26Z9POPYCZ4",
72
+ "name": "manner_of_inhumane_treatment",
73
+ "basename": "manner_of_inhumane_treatment",
74
+ "semanticType": "nominal",
75
+ "analyticType": "dimension"
76
+ },
77
+ {
78
+ "dragId": "gw_DUxe",
79
+ "fid": "GW_2AJUGNLE6MEPTZNHZAYG8IEPPSCE4MO1C",
80
+ "name": "cause_of_death",
81
+ "basename": "cause_of_death",
82
+ "semanticType": "nominal",
83
+ "analyticType": "dimension"
84
+ },
85
+ {
86
+ "dragId": "gw_wtxd",
87
+ "fid": "GW_1AW6HAPIZKT3VHC18C33DQM3968",
88
+ "name": "occupation",
89
+ "basename": "occupation",
90
+ "semanticType": "nominal",
91
+ "analyticType": "dimension"
92
+ },
93
+ {
94
+ "dragId": "gw_EEfP",
95
+ "fid": "GW_OMQ0RHNFV2PIFPFR6A0G",
96
+ "name": "spouse",
97
+ "basename": "spouse",
98
+ "semanticType": "nominal",
99
+ "analyticType": "dimension"
100
+ },
101
+ {
102
+ "dragId": "gw_AtER",
103
+ "fid": "GW_GGYX5I6DWC5400P4G",
104
+ "name": "longitude",
105
+ "basename": "longitude",
106
+ "semanticType": "quantitative",
107
+ "analyticType": "dimension"
108
+ },
109
+ {
110
+ "dragId": "gw_q-LU",
111
+ "fid": "GW_2BCEDJ2KYSIN2ZHS",
112
+ "name": "latitude",
113
+ "basename": "latitude",
114
+ "semanticType": "quantitative",
115
+ "analyticType": "dimension"
116
+ },
117
+ {
118
+ "fid": "GW_1K1RWLJMRKVG0",
119
+ "name": "father",
120
+ "semanticType": "nominal",
121
+ "analyticType": "dimension",
122
+ "basename": "father",
123
+ "dragId": "GW_BFrlUJKd"
124
+ },
125
+ {
126
+ "fid": "GW_CHAWSEHZCMSO4W",
127
+ "name": "sibling",
128
+ "semanticType": "nominal",
129
+ "analyticType": "dimension",
130
+ "basename": "sibling",
131
+ "dragId": "GW_enUhIyMF"
132
+ },
133
+ {
134
+ "fid": "GW_7NIDHER5RU8",
135
+ "name": "child",
136
+ "semanticType": "nominal",
137
+ "analyticType": "dimension",
138
+ "basename": "child",
139
+ "dragId": "GW_c7Noxzdq"
140
+ },
141
+ {
142
+ "fid": "GW_1NWT9FT2YJVAO",
143
+ "name": "mother",
144
+ "semanticType": "nominal",
145
+ "analyticType": "dimension",
146
+ "basename": "mother",
147
+ "dragId": "GW_YApxzLXH"
148
+ }
149
+ ],
150
+ "measures": [
151
+ {
152
+ "dragId": "gw_count_fid",
153
+ "fid": "gw_count_fid",
154
+ "name": "Row count",
155
+ "analyticType": "measure",
156
+ "semanticType": "quantitative",
157
+ "aggName": "sum",
158
+ "computed": true,
159
+ "expression": {
160
+ "op": "one",
161
+ "params": [],
162
+ "as": "gw_count_fid"
163
+ }
164
+ },
165
+ {
166
+ "dragId": "gw_mea_val_fid",
167
+ "fid": "GW_4KYB9KVI3CK37NQAUJ65UJWVK",
168
+ "name": "Measure values",
169
+ "analyticType": "measure",
170
+ "semanticType": "quantitative",
171
+ "aggName": "sum"
172
+ }
173
+ ],
174
+ "rows": [],
175
+ "columns": [],
176
+ "color": [
177
+ {
178
+ "dragId": "gw_I8g7",
179
+ "fid": "GW_2ZE8EA85LWGIU48N1GW",
180
+ "name": "class",
181
+ "basename": "class",
182
+ "semanticType": "nominal",
183
+ "analyticType": "dimension"
184
+ }
185
+ ],
186
+ "opacity": [],
187
+ "size": [],
188
+ "shape": [],
189
+ "radius": [],
190
+ "theta": [],
191
+ "longitude": [
192
+ {
193
+ "dragId": "gw_y2hj",
194
+ "fid": "GW_GGYX5I6DWC5400P4G",
195
+ "name": "longitude",
196
+ "basename": "longitude",
197
+ "semanticType": "quantitative",
198
+ "analyticType": "dimension"
199
+ }
200
+ ],
201
+ "latitude": [
202
+ {
203
+ "dragId": "gw_vshP",
204
+ "fid": "GW_2BCEDJ2KYSIN2ZHS",
205
+ "name": "latitude",
206
+ "basename": "latitude",
207
+ "semanticType": "quantitative",
208
+ "analyticType": "dimension"
209
+ }
210
+ ],
211
+ "geoId": [],
212
+ "details": [
213
+ {
214
+ "dragId": "gw_KQnh",
215
+ "fid": "GW_6RT7I4HK7073D9RPOESM5CH80",
216
+ "name": "residence",
217
+ "basename": "residence",
218
+ "semanticType": "nominal",
219
+ "analyticType": "dimension"
220
+ }
221
+ ],
222
+ "filters": [],
223
+ "text": []
224
+ },
225
+ "layout": {
226
+ "showActions": false,
227
+ "showTableSummary": false,
228
+ "stack": "stack",
229
+ "interactiveScale": false,
230
+ "zeroScale": true,
231
+ "size": {
232
+ "mode": "auto",
233
+ "width": 800,
234
+ "height": 800
235
+ },
236
+ "format": {},
237
+ "geoKey": "name",
238
+ "resolve": {
239
+ "x": false,
240
+ "y": false,
241
+ "color": false,
242
+ "opacity": false,
243
+ "shape": false,
244
+ "size": false
245
+ },
246
+ "scaleIncludeUnmatchedChoropleth": false
247
+ },
248
+ "visId": "gw_b4Sl",
249
+ "name": "Chart 1"
250
+ }
251
+ ]
data.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import pandas as pd
3
+
4
+ from SPARQLWrapper import SPARQLWrapper, JSON
5
+
6
+
7
+ endpoint_url = "https://query.wikidata.org/sparql"
8
+
9
+ query = """
10
+ # Places of residence of accused witches in Scotland 1563-1736
11
+ SELECT ?accusedurl ?item ?itemLabel ?residenceLabel ?genderLabel ?occupationLabel ?classLabel ?manner_of_inhumane_treatmentLabel ?place_of_detentionLabel ?cause_of_deathLabel ?fatherLabel ?motherLabel ?siblingLabel ?childLabel ?spouseLabel ?coords WHERE {
12
+ ?item wdt:P31 wd:Q5;
13
+ wdt:P4478 ?accused.
14
+ wd:P4478 wdt:P1630 ?formatterurl.
15
+ BIND(IRI(REPLACE(?accused, "^(.+)$", ?formatterurl)) AS ?accusedurl)
16
+ ?item wdt:P551 ?residence.
17
+ ?residence wdt:P625 ?coords.
18
+
19
+ OPTIONAL { ?item wdt:P21 ?gender. }
20
+ OPTIONAL { ?item wdt:P106 ?occupation. }
21
+ OPTIONAL { ?item wdt:P3716 ?class. }
22
+ SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
23
+ OPTIONAL { ?item wdt:P7160 ?manner_of_inhumane_treatment. }
24
+ OPTIONAL { ?item wdt:P2632 ?place_of_detention. }
25
+ OPTIONAL { ?item wdt:P509 ?cause_of_death. }
26
+ OPTIONAL { ?item wdt:P22 ?father. }
27
+ OPTIONAL { ?item wdt:P25 ?mother. }
28
+ OPTIONAL { ?item wdt:P3373 ?sibling. }
29
+ OPTIONAL { ?item wdt:P40 ?child. }
30
+ OPTIONAL { ?item wdt:P26 ?spouse. }
31
+ OPTIONAL { ?item wdt:P551 ?residence. }
32
+ }
33
+ """
34
+
35
+
36
+ def get_results(endpoint_url, query):
37
+ """
38
+ Obtain SPARQL query results.
39
+ """
40
+ user_agent = "WDQS-example Python/%s.%s" % (sys.version_info[0], sys.version_info[1])
41
+ # TODO adjust user agent; see https://w.wiki/CX6
42
+ sparql = SPARQLWrapper(endpoint_url, agent=user_agent)
43
+ sparql.setQuery(query)
44
+ sparql.setReturnFormat(JSON)
45
+ return sparql.query().convert()
46
+
47
+
48
+ def load_data():
49
+ """
50
+ Obtain data for accused witches charged with witchcraft.
51
+ """
52
+ lst = []
53
+ results = get_results(endpoint_url, query)
54
+ for result in results["results"]["bindings"]:
55
+ d = {}
56
+ for k, v in result.items():
57
+ d[k] = v['value']
58
+ lst.append(d)
59
+ data = pd.DataFrame(lst)
60
+ data.dropna(subset=['siblingLabel', 'spouseLabel', 'childLabel', 'fatherLabel', 'motherLabel'], how="all", inplace=True)
61
+ data['longitude'] = data['coords'].str.replace("Point", "").apply(lambda x: x.split()[0].lstrip("(")).astype(float)
62
+ data['latitude'] = data['coords'].str.replace("Point", "").apply(lambda x: x.split()[-1].rstrip(")")).astype(float)
63
+ data.drop(['coords'], axis=1, inplace=True)
64
+ data.columns = [col.replace("Label", "") if col != "itemLabel" else col for col in data.columns.tolist()]
65
+ return data
66
+
67
+
68
+ df = load_data()
network.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import networkx as nx
3
+ import matplotlib.pyplot as plt
4
+ from pyvis.network import Network
5
+
6
+ from data import df
7
+
8
+
9
+ def analysis():
10
+ G = nx.DiGraph()
11
+
12
+ # Define the columns to consider for relationships
13
+ relationship_columns = ['father', 'sibling', 'spouse', 'mother', 'child']
14
+
15
+ G.add_nodes_from(df["itemLabel"])
16
+
17
+ for index, row in df.iterrows():
18
+ main_entity = row['itemLabel']
19
+ for relationship in relationship_columns:
20
+ if pd.notna(row[relationship]):
21
+ G.add_edge(main_entity, row[relationship], relationship=str(relationship), label=relationship)
22
+
23
+ plt.figure(figsize=(50, 20)) # Set the size of the plot
24
+ # pos = nx.kamada_kawai_layout(G) # Compute the positions of the nodes
25
+
26
+ # # Draw the nodes and edges with labels
27
+ # nx.draw(G, pos, with_labels=True, node_size=20, node_color='skyblue', font_size=10, font_color='black', font_weight='bold', alpha=0.7)
28
+
29
+ # # Draw edge labels (the relationships)
30
+ # edge_labels = nx.get_edge_attributes(G, 'relationship')
31
+ # nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_color='red')
32
+
33
+ # plt.title('Relationship Graph with Labels')
34
+ # plt.axis('off') # Hide the axes for clarity
35
+ # plt.show()
36
+
37
+
38
+ net = Network(bgcolor="#222222", font_color="white", directed=True)
39
+ net.from_nx(G)
40
+
41
+ for node in net.nodes:
42
+ node["title"] = node["id"]
43
+ node["value"] = len(G[node["id"]])
44
+
45
+
46
+ for edge in net.edges:
47
+ # Set edge title to the relationship from your graph
48
+ relationship = G[edge["from"]][edge["to"]].get("relationship", "Unknown")
49
+ edge["title"] = relationship # This will show as a tooltip
50
+ edge["color"] = "blue"
51
+ edge["width"] = 2 if relationship != "Unknown" else 1
52
+
53
+ html = net.generate_html()
54
+ #need to remove ' from HTML
55
+ html = html.replace("'", "\"")
56
+
57
+ return f"""<iframe style="width: 100%; height: 1000px;margin:0 auto" name="result" allow="midi; geolocation; microphone; camera;
58
+ display-capture; encrypted-media;" sandbox="allow-modals allow-forms
59
+ allow-scripts allow-same-origin allow-popups
60
+ allow-top-navigation-by-user-activation allow-downloads" allowfullscreen=""
61
+ allowpaymentrequest="" frameborder="0" srcdoc='{html}'></iframe>"""
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ pandas
2
+ sparqlwrapper
3
+ gradio
4
+ pygwalker
5
+ datasets
6
+ pyvis
7
+ networkx