Spaces:
Sleeping
Sleeping
File size: 5,324 Bytes
5907aab c9875bd d229285 c9875bd d229285 5907aab d229285 5907aab d229285 c9875bd d229285 5907aab c9875bd 81a6f86 c9875bd d229285 c9875bd 5907aab c9875bd 81a6f86 d229285 5907aab c9875bd d229285 c9875bd 5907aab c9875bd 5907aab c9875bd 5907aab c9875bd 81a6f86 c9875bd d229285 c9875bd 5907aab c9875bd 5907aab c9875bd d229285 c9875bd 81a6f86 d229285 5907aab 81a6f86 d229285 81a6f86 5907aab 81a6f86 d229285 | 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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | import tabula
import pandas as pd
import io
import re
import os
from pathlib import Path
# --- CONFIGURATION ---
# Match telecom_anomaly_detection's pipeline exactly: every stage writes a CSV
# and the next stage reads it back. The CSV roundtrip normalizes dtypes and
# whitespace, which is what produces the anomaly counts the user expects.
DATA_BASE = Path("data")
RAW_CSV_DIR = DATA_BASE / "raw_csv_temp"
PROCESS_OUTPUT_DIR = DATA_BASE / "output_process_data"
STRICT_CLEAN_DIR = DATA_BASE / "strictly_cleaned_data"
def setup_folders():
"""Ensure all necessary output directories exist."""
for folder in [RAW_CSV_DIR, PROCESS_OUTPUT_DIR, STRICT_CLEAN_DIR]:
if not os.path.exists(folder):
os.makedirs(folder)
def extract_pdf_to_csv(pdf_path, base_name):
"""Stage 1: Convert PDF tables to a raw CSV file."""
csv_path = os.path.join(RAW_CSV_DIR, f"{base_name}_raw.csv")
try:
tabula.convert_into(pdf_path, csv_path, output_format="csv", pages="all", lattice=True)
return csv_path
except Exception as e:
print(f"Error extracting {pdf_path}: {e}")
return None
def process_telecom_data_complete(input_path, base_name):
"""Stage 2: Parse raw CSV and repair fragmented rows."""
try:
with open(input_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
except Exception as e:
print(f"Error reading {input_path}: {e}")
return None
header_idx = -1
for i, line in enumerate(lines):
if "event_date_time" in line:
header_idx = i
break
if header_idx == -1:
return None
# Customer info section (before the call-records header)
customer_text = "".join(lines[:header_idx])
df_customer = pd.read_csv(io.StringIO(customer_text))
df_customer.columns = [re.sub(r'[\r\n\s]+', ' ', c).strip() for c in df_customer.columns]
cust_path = os.path.join(PROCESS_OUTPUT_DIR, f'{base_name}_customer_info.csv')
df_customer.to_csv(cust_path, index=False)
# Call records
call_text = "".join(lines[header_idx:])
df_raw = pd.read_csv(io.StringIO(call_text))
df_raw.columns = [c.strip() for c in df_raw.columns]
main_col = df_raw.columns[0]
df_raw = df_raw[df_raw[main_col].astype(str).str.contains("event_date_time") == False]
fixed_rows = []
current_row = None
for _, row in df_raw.iterrows():
val = str(row[main_col]).strip()
if re.match(r'^\d{4}-\d{2}-\d{2}', val):
if current_row is not None:
fixed_rows.append(current_row)
current_row = row.copy()
elif current_row is not None:
current_row[main_col] = (str(current_row[main_col]) + " " + val).replace('\r', '').replace('\n', '')
for col in df_raw.columns:
if col != main_col and pd.api.types.is_string_dtype(df_raw[col]):
fragment = str(row[col]).strip()
if fragment.lower() != 'nan':
current_row[col] = (str(current_row[col]) + " " + fragment).replace('\r', '').replace('\n', ' ')
if current_row is not None:
fixed_rows.append(current_row)
df_calls = pd.DataFrame(fixed_rows)
df_calls[main_col] = df_calls[main_col].str.replace(r'[\r\n]+', '', regex=True).str.replace(r'\s+', ' ', regex=True).str.strip()
dt_series = pd.to_datetime(df_calls[main_col], errors='coerce')
df_calls['event_date'] = dt_series.dt.date
df_calls['event_time'] = dt_series.dt.time
df_calls.drop(columns=[main_col], inplace=True)
uppercase_cols = ['district', 'city', 'usage_type', 'usage_sub_type', 'calling_no', 'called_no', 'imei', 'location_id', 'msisdn']
for col in df_calls.columns:
c_low = col.lower()
if df_calls[col].dtype == 'object':
df_calls[col] = df_calls[col].astype(str).str.replace(r'[\r\n\s]+', ' ', regex=True).str.strip()
if c_low in uppercase_cols:
df_calls[col] = df_calls[col].str.upper()
elif c_low == 'region':
df_calls[col] = df_calls[col].str.title()
elif c_low == 'call_direction':
df_calls[col] = df_calls[col].str.title().replace({'Incomingmt': 'Incoming (MT)', '\(\(': '(', '\)\)': ')'}, regex=True)
ordered_cols = ['event_date', 'event_time'] + [c for c in df_calls.columns if c not in ['event_date', 'event_time']]
df_calls = df_calls[ordered_cols]
interim_path = os.path.join(PROCESS_OUTPUT_DIR, f'{base_name}_call_records_final.csv')
df_calls.to_csv(interim_path, index=False)
return interim_path
def strict_clean_data(file_path, base_name):
"""Stage 3: Final strict cleaning."""
df = pd.read_csv(file_path)
if 'duration' in df.columns and df['duration'].dtype == 'object':
df['duration'] = df['duration'].str.replace(',', '.').astype(float)
df['event_date'] = pd.to_datetime(df['event_date'], errors='coerce')
df['event_time_check'] = pd.to_datetime(df['event_time'], format='%H:%M:%S', errors='coerce')
df_cleaned = df.dropna().copy()
df_cleaned = df_cleaned.drop(columns=['event_time_check'])
final_path = os.path.join(STRICT_CLEAN_DIR, f'{base_name}_strictly_cleaned.csv')
df_cleaned.to_csv(final_path, index=False)
return final_path
|