├── requirements.txt ├── outputs ├── charts │ └── fraud_distribution.png └── fraud_summary.csv ├── src ├── create_db.py ├── utils.py ├── queries.sql └── detect_fraud_unsupervised.py ├── LICENSE └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | pandas>=2.0 2 | matplotlib>=3.7 3 | scikit-learn>=1.3 4 | -------------------------------------------------------------------------------- /outputs/charts/fraud_distribution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmirhosseinHonardoust/Fraud-Detection-SQL-Unsupervised/HEAD/outputs/charts/fraud_distribution.png -------------------------------------------------------------------------------- /src/create_db.py: -------------------------------------------------------------------------------- 1 | import argparse, sqlite3, pandas as pd 2 | from pathlib import Path 3 | parser=argparse.ArgumentParser(); parser.add_argument('--csv',required=True); parser.add_argument('--db',default='fraud.db'); args=parser.parse_args() 4 | df=pd.read_csv(args.csv) 5 | with sqlite3.connect(args.db) as con: 6 | con.execute('CREATE TABLE IF NOT EXISTS transactions (tx_id INTEGER,user_id TEXT,date TEXT,region TEXT,merchant TEXT,amount REAL)') 7 | df.to_sql('transactions',con,if_exists='replace',index=False) 8 | print('Loaded',args.csv,'->',args.db) 9 | -------------------------------------------------------------------------------- /src/utils.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | 5 | def ensure_outdir(p): 6 | p=Path(p); p.mkdir(parents=True, exist_ok=True); return p 7 | 8 | def save_csv(df,p): 9 | p=Path(p); p.parent.mkdir(parents=True, exist_ok=True); df.to_csv(p,index=False); return p 10 | 11 | def plot_hist(s,title,out): 12 | import matplotlib.pyplot as plt 13 | fig,ax=plt.subplots(figsize=(8,5)); ax.hist(s,bins=40); ax.set_title(title); ax.set_xlabel('Score'); ax.set_ylabel('Freq'); fig.tight_layout(); fig.savefig(out,dpi=150); plt.close(fig) 14 | -------------------------------------------------------------------------------- /src/queries.sql: -------------------------------------------------------------------------------- 1 | CREATE TEMP VIEW user_stats AS 2 | SELECT user_id, COUNT(*) tx_count, AVG(amount) avg_amount, SUM(amount) total_amount FROM transactions GROUP BY user_id; 3 | CREATE TEMP VIEW daily_user AS 4 | SELECT user_id, date, COUNT(*) daily_tx, SUM(amount) daily_amount FROM transactions GROUP BY user_id,date; 5 | SELECT t.tx_id,t.user_id,t.date,t.region,t.merchant,t.amount,us.tx_count,us.avg_amount,us.total_amount,COALESCE(du.daily_tx,0) daily_tx,COALESCE(du.daily_amount,0.0) daily_amount 6 | FROM transactions t 7 | LEFT JOIN user_stats us ON t.user_id=us.user_id 8 | LEFT JOIN daily_user du ON t.user_id=du.user_id AND t.date=du.date; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Amir 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/detect_fraud_unsupervised.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from __future__ import annotations 3 | import argparse 4 | import sqlite3 5 | from pathlib import Path 6 | 7 | import pandas as pd 8 | from sklearn.ensemble import IsolationForest 9 | 10 | from utils import ensure_outdir, save_csv, plot_hist 11 | 12 | 13 | def run_analysis(db_path: str | Path, sql_path: str | Path, outdir: str | Path) -> None: 14 | outdir = ensure_outdir(outdir) 15 | charts_dir = ensure_outdir(Path(outdir) / "charts") 16 | 17 | # Read the SQL file and split into statements. 18 | with open(sql_path, "r", encoding="utf-8") as f: 19 | sql_text = f.read() 20 | statements = [s.strip() for s in sql_text.split(";") if s.strip()] 21 | if not statements: 22 | raise RuntimeError("No SQL statements found in the provided file.") 23 | 24 | # Everything except the last statement are setup statements (e.g., CREATE VIEWs). 25 | setup_script = ";\n".join(statements[:-1]) + (";" if len(statements) > 1 else "") 26 | final_select = statements[-1] 27 | 28 | # Execute SQL: first the setup (if any), then the final SELECT. 29 | with sqlite3.connect(db_path) as con: 30 | if setup_script: 31 | con.executescript(setup_script) 32 | df = pd.read_sql_query(final_select, con) 33 | 34 | if df.empty: 35 | raise RuntimeError("Final SELECT returned no rows. Check your data and SQL.") 36 | 37 | # Features for anomaly detection 38 | feature_cols = ["amount", "tx_count", "avg_amount", "total_amount", "daily_tx", "daily_amount"] 39 | X = df[feature_cols].fillna(0) 40 | 41 | # Isolation Forest (unsupervised) 42 | model = IsolationForest( 43 | n_estimators=200, 44 | contamination=0.02, # ~2% anomalies 45 | random_state=7, 46 | ) 47 | # decision_function: higher = more normal → convert to [0,1] anomaly score where higher = more anomalous 48 | decision = model.fit(X).decision_function(X) 49 | df["anomaly_score"] = (-decision - decision.min()) / (decision.max() - decision.min() + 1e-9) 50 | 51 | # Rank by anomaly score 52 | df_sorted = df.sort_values("anomaly_score", ascending=False) 53 | 54 | # Save artifacts 55 | save_csv(df_sorted[["tx_id", "user_id", "amount", "anomaly_score"]], Path(outdir) / "fraud_scores.csv") 56 | top_summary = ( 57 | df_sorted.head(1000)[["user_id", "amount", "anomaly_score"]] 58 | .groupby("user_id") 59 | .agg(max_anomaly_score=("anomaly_score", "max"), total_amount=("amount", "sum")) 60 | .reset_index() 61 | .sort_values(["max_anomaly_score", "total_amount"], ascending=False) 62 | ) 63 | save_csv(top_summary, Path(outdir) / "fraud_summary.csv") 64 | 65 | # Plot histogram of anomaly scores 66 | plot_hist(df_sorted["anomaly_score"], "Anomaly Score Distribution", charts_dir / "fraud_distribution.png") 67 | 68 | print("Artifacts saved to:", str(Path(outdir).resolve())) 69 | 70 | 71 | def parse_args() -> argparse.Namespace: 72 | ap = argparse.ArgumentParser(description="Unsupervised fraud detection (Isolation Forest) with SQL features") 73 | ap.add_argument("--db", default="fraud.db", help="Path to SQLite database") 74 | ap.add_argument("--sql", default="src/queries.sql", help="Path to SQL file (feature engineering)") 75 | ap.add_argument("--outdir", default="outputs", help="Output directory") 76 | return ap.parse_args() 77 | 78 | 79 | def main() -> None: 80 | args = parse_args() 81 | run_analysis(args.db, args.sql, args.outdir) 82 | 83 | 84 | if __name__ == "__main__": 85 | main() 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fraud Detection [SQL + Python (Unsupervised)] 2 | 3 | Detect potentially fraudulent bank transactions using **SQL (SQLite)** for feature engineering and **Python** for unsupervised anomaly detection with Isolation Forest. 4 | 5 | --- 6 | 7 | ## Overview 8 | 9 | This project demonstrates a practical fraud detection workflow where no labeled data is available. 10 | It integrates **SQL-based data aggregation** with **machine learning anomaly detection**, showing how data engineers and analysts can uncover unusual transaction patterns in banking or financial systems. 11 | 12 | --- 13 | 14 | ## Workflow 15 | 16 | 1. **Load transaction data into SQLite** 17 | 2. **Run SQL feature engineering** 18 | - Compute user-level metrics (average amount, total amount, number of transactions) 19 | - Compute daily activity (daily totals and transaction counts) 20 | 3. **Apply Isolation Forest** to detect anomalies based on aggregated behavioral features 21 | 4. **Generate outputs** 22 | - Ranked anomaly scores 23 | - Summary tables 24 | - Distribution chart 25 | 26 | --- 27 | 28 | ## Project Structure 29 | 30 | ``` 31 | fraud-detection-sql-unsupervised/ 32 | ├─ README.md 33 | ├─ requirements.txt 34 | ├─ data/ 35 | │ └─ transactions.csv 36 | ├─ src/ 37 | │ ├─ create_db.py 38 | │ ├─ queries.sql 39 | │ ├─ detect_fraud_unsupervised.py 40 | │ └─ utils.py 41 | └─ outputs/ 42 | ├─ fraud_scores.csv 43 | ├─ fraud_summary.csv 44 | └─ charts/ 45 | └─ fraud_distribution.png 46 | ``` 47 | 48 | --- 49 | 50 | ## Dataset Schema 51 | 52 | | Column | Description | 53 | |---------|--------------| 54 | | tx_id | Transaction ID | 55 | | user_id | Unique user identifier | 56 | | date | Transaction date | 57 | | region | User region | 58 | | merchant | Merchant name or type | 59 | | amount | Transaction amount | 60 | 61 | --- 62 | 63 | ## SQL Feature Engineering 64 | 65 | Feature generation is handled by `src/queries.sql`. 66 | It builds temporary SQL views to calculate user statistics and daily activity. 67 | 68 | ```sql 69 | CREATE TEMP VIEW user_stats AS 70 | SELECT user_id, COUNT(*) AS tx_count, AVG(amount) AS avg_amount, SUM(amount) AS total_amount 71 | FROM transactions 72 | GROUP BY user_id; 73 | 74 | CREATE TEMP VIEW daily_user AS 75 | SELECT user_id, date, COUNT(*) AS daily_tx, SUM(amount) AS daily_amount 76 | FROM transactions 77 | GROUP BY user_id, date; 78 | 79 | SELECT t.tx_id, t.user_id, t.date, t.region, t.merchant, t.amount, 80 | us.tx_count, us.avg_amount, us.total_amount, 81 | COALESCE(du.daily_tx, 0) AS daily_tx, 82 | COALESCE(du.daily_amount, 0.0) AS daily_amount 83 | FROM transactions t 84 | LEFT JOIN user_stats us ON t.user_id = us.user_id 85 | LEFT JOIN daily_user du ON t.user_id = du.user_id AND t.date = du.date; 86 | ``` 87 | 88 | --- 89 | 90 | ## Machine Learning 91 | 92 | The unsupervised model uses **Isolation Forest** from scikit-learn. 93 | 94 | - Detects outliers based on feature deviation 95 | - Flags top anomalies (typically 2–3% of all transactions) 96 | - Produces a normalized `anomaly_score` between 0 and 1 97 | 98 | --- 99 | 100 | ## Visualization 101 | 102 | ### Anomaly Score Distribution 103 | fraud_distribution 104 | 105 | This histogram visualizes the distribution of anomaly scores across transactions. 106 | The right-side tail represents potentially fraudulent or irregular activities. 107 | 108 | --- 109 | 110 | ## Tools & Libraries 111 | 112 | | Tool | Purpose | 113 | |------|----------| 114 | | **SQLite** | Feature engineering and querying | 115 | | **Python** | Analysis and ML modeling | 116 | | **pandas** | Data manipulation | 117 | | **scikit-learn** | Isolation Forest implementation | 118 | | **matplotlib** | Visualization | 119 | 120 | --- 121 | 122 | ## Usage 123 | 124 | ### Load Data into SQLite 125 | ```bash 126 | python src/create_db.py --csv data/transactions.csv --db fraud.db 127 | ``` 128 | 129 | ### Run Detection 130 | ```bash 131 | python src/detect_fraud_unsupervised.py --db fraud.db --sql src/queries.sql --outdir outputs 132 | ``` 133 | 134 | --- 135 | 136 | ## Outputs 137 | 138 | | File | Description | 139 | |------|--------------| 140 | | `fraud_scores.csv` | Ranked transactions with anomaly scores | 141 | | `fraud_summary.csv` | User-level fraud summary | 142 | | `fraud_distribution.png` | Histogram of anomaly scores | 143 | 144 | --- 145 | 146 | ## Conclusion 147 | 148 | This project showcases a complete unsupervised anomaly detection pipeline. 149 | It demonstrates how **SQL + Python** can work together to identify rare or irregular financial behaviors, a foundation for fraud prevention and risk analysis systems. 150 | -------------------------------------------------------------------------------- /outputs/fraud_summary.csv: -------------------------------------------------------------------------------- 1 | user_id,max_anomaly_score,total_amount 2 | U1227,0.8363353448551203,8216.97 3 | U1252,0.8153954855439927,5459.52 4 | U1373,0.8049028453558629,4006.79 5 | U1173,0.8007303039273692,7664.11 6 | U1371,0.800494314216039,4636.44 7 | U1359,0.7877507428910638,4461.75 8 | U1370,0.7824885091343915,3206.08 9 | U1052,0.7666953481621511,4155.96 10 | U1230,0.7665249929757668,2465.45 11 | U1332,0.7657496271491363,2922.1 12 | U1300,0.7609587805086591,1587.54 13 | U1165,0.7590951504085568,3977.83 14 | U1138,0.7516274118833919,5329.7300000000005 15 | U1197,0.7483274222985637,5841.62 16 | U1374,0.7480906471750522,6457.5199999999995 17 | U1153,0.7438739222207231,2571.1099999999997 18 | U1016,0.7415554892186003,4709.24 19 | U1139,0.7407066083957107,1518.76 20 | U1217,0.729019097348292,3242.3199999999997 21 | U1284,0.7280328516973414,5541.91 22 | U1059,0.7275104633191549,2805.99 23 | U1093,0.7248406086080876,4175.51 24 | U1239,0.7151930129094637,2178.4900000000002 25 | U1043,0.7140234062638144,4495.0 26 | U1223,0.7126833589251179,1709.84 27 | U1007,0.7102598487709739,1906.57 28 | U1348,0.7077954976812716,3169.67 29 | U1291,0.7067918663154639,3215.7400000000002 30 | U1075,0.7053723259720741,3301.84 31 | U1306,0.703042954880511,2815.29 32 | U1118,0.701614563783981,1663.42 33 | U1132,0.7001634494501328,3490.36 34 | U1392,0.6999938918846906,2043.25 35 | U1180,0.6996724195675511,4193.93 36 | U1391,0.6994821933744066,2094.48 37 | U1353,0.6994090487173781,2295.23 38 | U1163,0.6988772222577367,1356.52 39 | U1389,0.6974184599833021,2077.79 40 | U1338,0.6973675966854249,3792.87 41 | U1382,0.6972485146387173,3254.26 42 | U1343,0.6972271053732031,3524.54 43 | U1160,0.6964319583279367,4406.52 44 | U1172,0.6961564517353606,1311.57 45 | U1384,0.6954862602653463,1313.94 46 | U1222,0.6951041231585673,3115.6 47 | U1184,0.6949556914509863,2102.92 48 | U1215,0.6948198995368333,3671.33 49 | U1039,0.6903334669432722,1814.02 50 | U1177,0.6898645184239459,4038.88 51 | U1288,0.6883202294752572,2054.37 52 | U1100,0.6879339040218073,1389.01 53 | U1248,0.6849119313807451,2692.2200000000003 54 | U1387,0.6828600578931868,1204.85 55 | U1208,0.6821757316423914,2378.8 56 | U1195,0.6819507665348955,2539.31 57 | U1147,0.6807445751986606,2018.03 58 | U1381,0.679938847587858,3172.45 59 | U1321,0.6798400280600921,2424.06 60 | U1376,0.6788391455970663,1366.3 61 | U1136,0.677252215046097,3780.42 62 | U1286,0.6759483251691365,3407.6800000000003 63 | U1385,0.6753447244939464,1524.2 64 | U1156,0.6749313027019349,3764.91 65 | U1273,0.6745561517303227,2886.6800000000003 66 | U1209,0.6741501610893893,2137.36 67 | U1202,0.6726463764116692,2486.17 68 | U1297,0.6725574455952811,1607.74 69 | U1069,0.6712500054997418,1860.9299999999998 70 | U1362,0.670963425025362,3851.42 71 | U1254,0.6695877686629796,1757.0800000000002 72 | U1260,0.6679268161387001,1330.13 73 | U1116,0.6673644937458749,4454.59 74 | U1337,0.6666776545431898,2594.31 75 | U1125,0.6659839065697234,2293.12 76 | U1070,0.6656908320756402,3068.68 77 | U1290,0.6622663880990156,2631.65 78 | U1021,0.6610427878418742,3717.13 79 | U1128,0.6596382634103155,671.19 80 | U1257,0.6592534348247622,2875.35 81 | U1368,0.6587388713646923,2025.8600000000001 82 | U1196,0.658569454709716,2085.87 83 | U1030,0.6577637122168435,3356.61 84 | U1251,0.6567229692478548,2248.8900000000003 85 | U1175,0.6561238312843837,2710.37 86 | U1019,0.6558509648267344,2114.87 87 | U1201,0.6547447302384989,2012.04 88 | U1313,0.6536000983911859,1050.72 89 | U1304,0.6513993556578002,1701.1599999999999 90 | U1068,0.6511154038223821,1978.58 91 | U1218,0.6504492984138255,3200.01 92 | U1131,0.6499629105358522,1291.96 93 | U1393,0.649160868048928,1222.25 94 | U1048,0.6488283118477454,2689.7200000000003 95 | U1336,0.6485090841838572,1961.9299999999998 96 | U1316,0.6483557648649437,1918.71 97 | U1182,0.6476451010560786,2320.65 98 | U1335,0.6472858681697369,1285.3 99 | U1185,0.6458676833374717,2102.81 100 | U1119,0.6451039617466967,3297.99 101 | U1325,0.6448271025781721,1157.24 102 | U1193,0.643967130375308,1109.81 103 | U1314,0.6425589268610205,2425.17 104 | U1350,0.6423662992044217,4497.27 105 | U1377,0.6414997164701821,2397.05 106 | U1062,0.6397100140170267,1852.3200000000002 107 | U1057,0.638393181061343,745.41 108 | U1360,0.6383478208209505,2398.59 109 | U1178,0.6360329063319083,2029.0299999999997 110 | U1283,0.6352794412954249,743.12 111 | U1109,0.6343197576237987,2286.1000000000004 112 | U1053,0.6338215934094596,2001.5 113 | U1085,0.6333244737977942,2818.49 114 | U1296,0.63116517910674,1185.63 115 | U1397,0.6299105382439667,2724.26 116 | U1143,0.6284372184429959,1364.31 117 | U1146,0.6280866228982067,932.14 118 | U1305,0.6246200826542361,1646.17 119 | U1122,0.6244630479600686,1289.66 120 | U1067,0.624071313263734,1137.66 121 | U1276,0.6239221643385019,3374.61 122 | U1258,0.6239163441837187,1190.07 123 | U1212,0.6233410302037359,1100.42 124 | U1190,0.6210367607583708,1272.05 125 | U1270,0.6204884904807678,1937.5 126 | U1331,0.6201161129759426,2603.89 127 | U1363,0.6199966513212586,876.59 128 | U1250,0.6198619645460025,2024.77 129 | U1237,0.6198551316388276,1785.02 130 | U1001,0.618914568642886,1336.76 131 | U1049,0.6179728184789066,2086.37 132 | U1045,0.61742165836188,2187.46 133 | U1308,0.6173033068686915,2616.95 134 | U1263,0.6136960789375636,1085.07 135 | U1396,0.6131246026025664,2143.6800000000003 136 | U1323,0.6129714177772195,1948.28 137 | U1249,0.6116397207807432,1719.51 138 | U1009,0.6109381904319166,1103.84 139 | U1333,0.6107983474787748,1788.0500000000002 140 | U1317,0.6094877650925516,2092.02 141 | U1123,0.6093447286924377,2405.29 142 | U1200,0.6076777683595271,1588.7 143 | U1079,0.6074836695509409,2741.0099999999998 144 | U1235,0.6061756325782032,2064.16 145 | U1089,0.6061498988917341,1999.6100000000001 146 | U1055,0.6057835540681885,1072.31 147 | U1213,0.6053798583096298,2085.79 148 | U1038,0.6036912547223974,1986.79 149 | U1086,0.603593050172492,591.64 150 | U1256,0.6035686030760609,2581.5299999999997 151 | U1054,0.6023572127323069,3771.18 152 | U1002,0.6010993100789828,1164.42 153 | U1031,0.5979644242491943,2314.12 154 | U1299,0.5925502273499875,992.03 155 | U1004,0.5918571392969529,1677.6699999999998 156 | U1072,0.5914810819755841,2315.49 157 | U1191,0.5914112694658759,1049.37 158 | U1036,0.5900260189680953,2642.96 159 | U1126,0.5899869759897806,2617.47 160 | U1133,0.5892747001650444,1067.82 161 | U1188,0.58820360162892,2177.1800000000003 162 | U1287,0.5860988109823566,1045.71 163 | U1117,0.5859638630755054,1023.83 164 | U1372,0.5852622869505929,1099.85 165 | U1081,0.5850515681762668,1792.81 166 | U1032,0.5842507182381181,828.52 167 | U1066,0.5829568579328911,1945.17 168 | U1307,0.5821649502426932,2916.38 169 | U1366,0.5809566270533244,1050.35 170 | U1232,0.5784974774157788,963.38 171 | U1210,0.5770465712864253,1727.87 172 | U1042,0.5765392970099575,1081.2 173 | U1216,0.575584001247524,2243.46 174 | U1121,0.5753456688913186,1740.81 175 | U1060,0.5715719832113249,854.76 176 | U1253,0.5703410036338261,2394.33 177 | U1025,0.5698956791033851,2110.82 178 | U1171,0.5685224029742711,1411.5500000000002 179 | U1164,0.5678304159535266,512.52 180 | U1179,0.5666439054648373,1125.08 181 | U1346,0.5663572763376403,1002.12 182 | U1061,0.5659169429490446,1750.43 183 | U1243,0.565040687606807,1890.7199999999998 184 | U1334,0.5639596292407429,1198.6100000000001 185 | U1113,0.5636778784630926,2148.0299999999997 186 | U1080,0.5633034852332883,2950.48 187 | U1264,0.5630547734900552,1835.56 188 | U1003,0.5627532893879112,981.93 189 | U1046,0.5624395979278319,784.4 190 | U1044,0.5617338497197055,1039.14 191 | U1064,0.5612298994858809,1589.75 192 | U1379,0.5607801061501222,2311.4 193 | U1224,0.5566575665121057,1257.23 194 | U1091,0.5549234913684233,815.72 195 | U1087,0.5548457470090756,2410.42 196 | U1000,0.5544011745250429,678.35 197 | U1281,0.5530160905303487,1026.43 198 | U1328,0.5520977953395121,1674.02 199 | U1099,0.5520200390696535,2408.46 200 | U1103,0.5507013549715556,871.15 201 | U1176,0.5487694601359355,1027.05 202 | U1012,0.5479758084903834,1481.3600000000001 203 | U1194,0.5465304173087534,916.85 204 | U1364,0.5451600890028784,2139.98 205 | U1155,0.5450766033684089,795.12 206 | U1219,0.544801072543848,707.53 207 | U1162,0.5445552609254346,824.45 208 | U1168,0.5443669649525477,926.73 209 | U1341,0.5433850475485756,950.05 210 | U1259,0.5425878842060248,2862.89 211 | U1198,0.5412374759590258,1598.27 212 | U1285,0.5401877099022696,1465.25 213 | U1277,0.5395984725538883,1151.8200000000002 214 | U1111,0.5369083003604277,668.28 215 | U1380,0.5365446894723418,902.3299999999999 216 | U1352,0.5345583247445319,2542.36 217 | U1229,0.5345165435908573,857.03 218 | U1154,0.5344793987067484,1467.72 219 | U1027,0.5342659568129743,1430.6599999999999 220 | U1221,0.5301617531550168,1707.32 221 | U1166,0.5285698259474962,1146.62 222 | U1293,0.5284577729306621,793.88 223 | U1102,0.5277662573361204,1931.85 224 | U1124,0.5273722998815866,2336.45 225 | U1035,0.5271461042100803,621.38 226 | U1014,0.5267827615587511,841.45 227 | U1327,0.5257488479918832,1196.8200000000002 228 | U1084,0.5253340186191198,819.13 229 | U1158,0.5246713092485302,946.27 230 | U1345,0.5236471272696991,662.72 231 | U1034,0.5229379714978517,1535.79 232 | U1322,0.5222423884085697,965.85 233 | U1174,0.5220121282978543,850.91 234 | U1183,0.5216661848525928,414.4 235 | U1056,0.5213076065872879,786.33 236 | U1148,0.5198369913667539,1395.21 237 | U1294,0.5197772639364587,1611.34 238 | U1192,0.5187691936187447,1164.34 239 | U1107,0.5186054246246519,683.67 240 | U1228,0.5168536767962181,1681.55 241 | U1149,0.5151024315739853,782.36 242 | U1161,0.5141342840778491,850.91 243 | U1275,0.5135405410977959,1424.88 244 | U1033,0.5125719544969972,906.24 245 | U1015,0.5120068415937671,875.3 246 | U1390,0.5115395656764977,1588.8899999999999 247 | U1324,0.5113407199708371,1593.35 248 | U1361,0.510789108594194,805.74 249 | U1356,0.5093776641940647,1595.94 250 | U1246,0.5081938870423834,968.74 251 | U1289,0.505106508771337,1135.52 252 | U1231,0.5029057783965809,620.74 253 | U1006,0.49973227645463336,540.78 254 | U1134,0.499456138386138,1685.9099999999999 255 | U1135,0.4991713025891596,745.62 256 | U1367,0.4985144255924403,769.01 257 | U1076,0.49641580991516865,813.29 258 | U1279,0.49638897341918115,1533.9 259 | U1114,0.494114160595604,830.92 260 | U1187,0.49386316202813774,682.47 261 | U1395,0.4915942247097089,834.63 262 | U1206,0.49124749035843107,733.53 263 | U1040,0.48788933577322113,650.29 264 | U1369,0.48664756792114056,736.88 265 | U1399,0.48633639763895326,623.85 266 | U1074,0.486096881202898,836.88 267 | U1108,0.4852241674788576,849.73 268 | U1261,0.48485599168393034,804.86 269 | U1078,0.4842751920172629,724.67 270 | U1151,0.4822862051869622,599.56 271 | U1278,0.4802943450989268,1212.3 272 | U1383,0.47951743708129935,716.54 273 | U1265,0.4794556338419455,658.24 274 | U1357,0.4787714567998479,734.44 275 | U1344,0.47827248913130943,670.12 276 | U1041,0.4782489485550902,755.78 277 | U1238,0.4774192139235316,595.86 278 | U1351,0.4763437583241316,5.0 279 | U1271,0.4759695887872447,1129.74 280 | U1349,0.47572388795675896,661.09 281 | U1309,0.4751103446182952,728.42 282 | U1312,0.47375649490696464,757.48 283 | U1008,0.47326735165688294,761.71 284 | U1388,0.4728656546315356,637.95 285 | U1005,0.47262886858330067,576.68 286 | U1170,0.4710889270546118,733.46 287 | U1292,0.4698015838392983,749.55 288 | U1024,0.4693295446809379,512.46 289 | U1051,0.46799763011073564,722.4 290 | U1268,0.4670497759877599,641.12 291 | U1104,0.46586826044935326,710.84 292 | U1241,0.46389936434301193,646.55 293 | U1077,0.460008347712235,804.06 294 | U1140,0.45872831688462185,651.03 295 | U1150,0.456085460991997,668.23 296 | --------------------------------------------------------------------------------