-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path1_create_graph.py
More file actions
104 lines (80 loc) · 3.28 KB
/
Copy path1_create_graph.py
File metadata and controls
104 lines (80 loc) · 3.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
"""
Constructs a bipartite network from a chant dataset.
Includes data loading, cleaning, and graph construction.
"""
from collections import Counter
import pandas as pd
import numpy as np
import graph_tool.all as gt
def load_and_clean_data(path):
"""Load chant dataset and print basic stats on missing data and string columns."""
df = pd.read_csv(path)
report_missing(df)
report_string_distributions(df)
return df
def report_missing(df):
"""Print missing value statistics."""
missing = df.isnull().sum()
percent = (missing / len(df)) * 100
out = pd.DataFrame({'Missing Values': missing, 'Percentage': percent})
print(out[out['Missing Values'] > 0].sort_values('Missing Values', ascending=False))
def report_string_distributions(df):
"""Print top values in string columns."""
for col in df.select_dtypes(include='object'):
print(f"\n{col} ({df[col].nunique()} unique):")
print(df[col].value_counts().head(10))
def clean_string_columns(df, columns):
"""Normalize whitespace and replace placeholder strings with NaN."""
df = df.copy()
for col in columns:
if col in df and df[col].dtype == 'object':
df[col] = df[col].astype(str).str.strip().str.replace(r'\s+', ' ', regex=True)
df[col] = df[col].replace(['nan', 'None', 'NaN', 'none', 'NULL', 'null'], np.nan)
return df
def standardize_capitalization(df, columns):
"""Capitalize values in selected columns based on most common casing pattern."""
df = df.copy()
for col in columns:
if col in df and df[col].dtype == 'object':
patterns = Counter()
for val in df[col].dropna().unique():
if isinstance(val, str):
patterns[val.lower()] += 1
mapping = {k: max((v for v in patterns if v.lower() == k), key=patterns.get)
for k in patterns}
df[col] = df[col].str.lower().map(mapping).fillna(df[col])
return df
def build_bipartite_graph(df, chant_col, ms_col):
"""Create a bipartite graph from chants and manuscripts."""
g = gt.Graph(directed=False)
chant_map, ms_map = {}, {}
vprop_name = g.new_vertex_property("string")
vprop_type = g.new_vertex_property("string")
for _, row in df.dropna(subset=[chant_col, ms_col]).iterrows():
chant = row[chant_col]
ms = row[ms_col]
if chant not in chant_map:
chant_v = g.add_vertex()
chant_map[chant] = chant_v
vprop_name[chant_v] = chant
vprop_type[chant_v] = 'chant'
if ms not in ms_map:
ms_v = g.add_vertex()
ms_map[ms] = ms_v
vprop_name[ms_v] = ms
vprop_type[ms_v] = 'manuscript'
g.add_edge(chant_map[chant], ms_map[ms])
g.vp["name"] = vprop_name
g.vp["type"] = vprop_type
return g
def save_graph(g, path):
"""Save graph to file."""
g.save(path)
if __name__ == "__main__":
input_path = "dataset/proper_of_mass.csv"
output_path = "processed/trope_manuscript_bipartite.gt"
df = load_and_clean_data(input_path)
df = clean_string_columns(df, ["chant", "source"])
df = standardize_capitalization(df, ["chant", "source"])
graph = build_bipartite_graph(df, "chant", "source")
save_graph(graph, output_path)