Spaces:
Sleeping
Sleeping
Arya Patil
commited on
Commit
·
85fd14e
1
Parent(s):
30d6b96
Initial deployment of CausalBox app
Browse files- Dockerfile +29 -12
- LICENSE +21 -0
- README.md +1 -20
- data/sample_dataset.csv +1001 -0
- main.py +38 -0
- nginx.conf +55 -0
- requirements.txt +10 -2
- routers/__pycache__/discover_routes.cpython-310.pyc +0 -0
- routers/__pycache__/intervene_routes.cpython-310.pyc +0 -0
- routers/__pycache__/preprocess_routes.cpython-310.pyc +0 -0
- routers/__pycache__/treatment_routes.cpython-310.pyc +0 -0
- routers/__pycache__/visualize_routes.cpython-310.pyc +0 -0
- routers/discover_routes.py +43 -0
- routers/intervene_routes.py +54 -0
- routers/preprocess_routes.py +56 -0
- routers/treatment_routes.py +54 -0
- routers/visualize_routes.py +43 -0
- scripts/generate_data.py +29 -0
- src/streamlit_app.py +0 -40
- start.sh +24 -0
- streamlit_app.py +308 -0
- utils/__pycache__/casual_algorithms.cpython-310.pyc +0 -0
- utils/__pycache__/do_calculus.cpython-310.pyc +0 -0
- utils/__pycache__/graph_utils.cpython-310.pyc +0 -0
- utils/__pycache__/preprocessor.cpython-310.pyc +0 -0
- utils/__pycache__/treatment_effects.cpython-310.pyc +0 -0
- utils/auto_refresh.py +0 -0
- utils/casual_algorithms.py +64 -0
- utils/do_calculus.py +52 -0
- utils/graph_utils.py +60 -0
- utils/preprocessor.py +57 -0
- utils/treatment_effects.py +63 -0
Dockerfile
CHANGED
@@ -1,21 +1,38 @@
|
|
|
|
1 |
FROM python:3.9-slim
|
2 |
|
|
|
3 |
WORKDIR /app
|
4 |
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
&& rm -rf /var/lib/apt/lists/*
|
11 |
|
12 |
-
|
13 |
-
COPY
|
|
|
14 |
|
15 |
-
|
|
|
|
|
|
|
|
|
16 |
|
17 |
-
|
|
|
|
|
18 |
|
19 |
-
|
|
|
|
|
20 |
|
21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use a Python base image with a good balance of size and features
|
2 |
FROM python:3.9-slim
|
3 |
|
4 |
+
# Set the working directory inside the container
|
5 |
WORKDIR /app
|
6 |
|
7 |
+
# Install system dependencies (Nginx, etc.)
|
8 |
+
# Run apt-get update first, then install packages, then clean up apt cache
|
9 |
+
RUN apt-get update && \
|
10 |
+
apt-get install -y --no-install-recommends nginx && \
|
11 |
+
rm -rf /var/lib/apt/lists/*
|
|
|
12 |
|
13 |
+
# Copy the combined requirements file and install Python dependencies
|
14 |
+
COPY requirements.txt .
|
15 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
16 |
|
17 |
+
# Copy your Flask backend and Streamlit frontend code
|
18 |
+
COPY flask_backend/ ./flask_backend/
|
19 |
+
COPY streamlit_frontend/ ./streamlit_frontend/
|
20 |
+
# If you have a sample dataset you want to include
|
21 |
+
COPY data/ ./data/
|
22 |
|
23 |
+
# Copy Nginx configuration and startup script
|
24 |
+
COPY nginx.conf /etc/nginx/sites-available/default
|
25 |
+
COPY start.sh .
|
26 |
|
27 |
+
# Ensure Nginx uses our config
|
28 |
+
RUN ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default && \
|
29 |
+
rm -rf /etc/nginx/sites-enabled/default.bak
|
30 |
|
31 |
+
# Make the startup script executable
|
32 |
+
RUN chmod +x start.sh
|
33 |
+
|
34 |
+
# Expose the port Nginx will listen on (Hugging Face Spaces will expose this to the internet)
|
35 |
+
EXPOSE 7860
|
36 |
+
|
37 |
+
# Command to run on container startup
|
38 |
+
CMD ["./start.sh"]
|
LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2025 Arya Patil
|
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.
|
README.md
CHANGED
@@ -1,20 +1 @@
|
|
1 |
-
|
2 |
-
title: CausalBox
|
3 |
-
emoji: 🚀
|
4 |
-
colorFrom: red
|
5 |
-
colorTo: red
|
6 |
-
sdk: docker
|
7 |
-
app_port: 8501
|
8 |
-
tags:
|
9 |
-
- streamlit
|
10 |
-
pinned: false
|
11 |
-
short_description: This project is designed for data scientists, researchers, a
|
12 |
-
license: mit
|
13 |
-
---
|
14 |
-
|
15 |
-
# Welcome to Streamlit!
|
16 |
-
|
17 |
-
Edit `/src/streamlit_app.py` to customize this app to your heart's desire. :heart:
|
18 |
-
|
19 |
-
If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
|
20 |
-
forums](https://discuss.streamlit.io).
|
|
|
1 |
+
# casualbox
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data/sample_dataset.csv
ADDED
@@ -0,0 +1,1001 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
StudyHours,TuitionHours,ParentalEducation,SchoolType,FinalExamScore
|
2 |
+
10.993428306022466,6.399355436586002,Medium,Public,91.29923038773146
|
3 |
+
9.723471397657631,5.924633682912769,Low,Public,75.91360822304543
|
4 |
+
11.295377076201385,5.059630369920174,Medium,Public,69.87670713396345
|
5 |
+
13.046059712816051,4.353063222294426,Low,Public,85.51675275982006
|
6 |
+
9.531693250553328,5.69822331361359,Low,Public,84.56054181454806
|
7 |
+
9.531726086101639,5.39348538542175,Low,Public,75.63488134273703
|
8 |
+
13.158425631014783,5.895193220027732,Medium,Private,81.63445369137285
|
9 |
+
11.534869458305817,5.635171801681969,High,Private,79.3361995922461
|
10 |
+
9.061051228130095,6.049552715319335,High,Private,81.44745836489537
|
11 |
+
11.08512008717193,4.464764788439432,Medium,Public,76.5950826505781
|
12 |
+
9.073164614375075,6.317394065634326,High,Private,79.26622182891586
|
13 |
+
9.068540492859487,5.1975996046924,High,Public,76.07249497145726
|
14 |
+
10.48392454313207,7.0752608726252655,Low,Public,81.20384233308822
|
15 |
+
6.173439510684404,4.310812181910432,Low,Public,72.31955892799562
|
16 |
+
6.5501643349739345,6.73596380316525,Medium,Public,70.81137487537477
|
17 |
+
8.875424941518055,5.197910783462648,Medium,Private,73.39478325731868
|
18 |
+
7.974337759331153,4.348581996385551,High,Public,76.45956276323939
|
19 |
+
10.628494665190548,4.5161141659456785,Low,Private,73.09653395845635
|
20 |
+
8.183951848957578,4.67965269180568,Medium,Public,68.98628178151203
|
21 |
+
7.175392597329417,5.424165946401916,Medium,Private,66.18546265754375
|
22 |
+
12.931297537843108,5.5228354880355,Medium,Private,88.39190287412222
|
23 |
+
9.548447399026928,4.426299996061421,Medium,Private,75.21015995173964
|
24 |
+
10.135056409375848,4.975645407739017,Medium,Private,71.16550519996488
|
25 |
+
7.150503627573086,7.142270358611864,Medium,Private,79.28345560460932
|
26 |
+
8.911234550949635,6.727543170100711,High,Private,75.13607304917315
|
27 |
+
10.221845179419732,5.4363236696740325,Low,Public,74.23978546916608
|
28 |
+
7.698012845155395,5.0380034781682,Low,Private,71.97463811317833
|
29 |
+
10.751396036691343,5.120031326718261,Medium,Public,86.57645711079954
|
30 |
+
8.79872262016239,5.613517972730417,Low,Private,74.04291121693757
|
31 |
+
9.416612500413446,3.9772074348015947,Low,Public,77.67573047360722
|
32 |
+
8.796586775541206,4.742623462576655,Low,Private,78.34250884720215
|
33 |
+
13.704556369017876,3.3314159261224106,Medium,Private,84.6226031172514
|
34 |
+
9.973005550524132,5.399223122605279,Medium,Public,84.4002040333275
|
35 |
+
7.884578142088199,5.647195939702741,Low,Private,70.70437017056912
|
36 |
+
11.645089824206378,4.516813537700626,Low,Private,74.84872573971201
|
37 |
+
7.5583127000579555,6.573986763290033,Medium,Private,69.66277226492598
|
38 |
+
10.417727190009511,3.774234336997659,High,Public,75.77446380901655
|
39 |
+
6.080659752240448,3.53562511979018,Low,Private,62.72952389496353
|
40 |
+
7.3436279022031385,5.224451818559972,High,Public,73.26163809157288
|
41 |
+
10.393722471738247,6.047098302612154,Low,Private,80.24303449601918
|
42 |
+
11.476933159990821,6.6839276914578685,High,Private,88.07262437309865
|
43 |
+
10.34273656237994,4.54111573713106,High,Public,78.95286827163666
|
44 |
+
9.76870343522352,6.078680833431296,High,Private,76.57466884128674
|
45 |
+
9.397792608821423,4.961491530499293,Medium,Private,78.29206853555519
|
46 |
+
7.042956019265145,4.82737270005918,High,Public,66.17600098912936
|
47 |
+
8.560311583210582,5.883659937486672,High,Private,75.75540963252959
|
48 |
+
9.078722458080424,5.65232287841342,High,Public,78.23293540175888
|
49 |
+
12.11424445243783,3.4236078430157506,Low,Public,80.01048866014992
|
50 |
+
10.687236579136922,6.476540349725896,High,Public,83.1646091575426
|
51 |
+
6.473919689274532,6.380091354147451,Low,Private,69.29774633120628
|
52 |
+
10.648167938789591,4.374437298490471,High,Public,83.61814883936246
|
53 |
+
9.229835439167367,5.395803533437409,High,Public,85.66422092546938
|
54 |
+
8.646155999388082,5.494030186282739,Medium,Private,67.51997655390731
|
55 |
+
11.223352577681736,5.260673765823907,Low,Public,74.25972582432809
|
56 |
+
12.061999044991902,4.44969484615458,High,Private,81.01395539933759
|
57 |
+
11.862560238232398,4.328376632061948,Low,Private,78.93493052179335
|
58 |
+
8.321564953554724,4.974445929008555,Low,Private,83.5868608294862
|
59 |
+
9.38157524829757,6.172729019259364,Medium,Private,79.12116564260648
|
60 |
+
10.662526862807129,5.543600154594432,Medium,Public,86.60318022613569
|
61 |
+
11.951090254244718,4.629385667913894,Medium,Public,70.60063957809228
|
62 |
+
9.04165152430942,5.771698710648344,Medium,Public,80.81169853125313
|
63 |
+
9.628682046672365,2.1514573793699245,Medium,Public,68.90696606610801
|
64 |
+
7.787330051987944,6.1487657003721035,Medium,Public,71.14009580643575
|
65 |
+
7.607586751838658,3.2602862212219,Medium,Private,68.86381270344955
|
66 |
+
11.625051644788396,4.637559058596868,Medium,Public,73.08352367881665
|
67 |
+
12.712480057141645,3.880330105373856,High,Public,83.45938289895334
|
68 |
+
9.855979756839332,3.705318524279094,Low,Public,76.22351213368128
|
69 |
+
12.007065795784047,6.16082678737852,Medium,Private,84.58633301357222
|
70 |
+
10.723272050095268,4.5322987986881085,Medium,Private,82.68014740888722
|
71 |
+
8.709760490789751,5.346503881730715,Low,Public,83.01318703514633
|
72 |
+
10.722791211016828,4.953079421120816,High,Private,77.05664630827714
|
73 |
+
13.076073132931938,5.477040827223122,Low,Public,86.75446070538094
|
74 |
+
9.928347921780096,5.076821891060256,Low,Public,78.93686972182546
|
75 |
+
13.129287311628012,3.7170077758114304,Medium,Private,83.72215881861787
|
76 |
+
4.760509791820511,5.99626681944714,Low,Private,70.47840813514165
|
77 |
+
11.643805008750448,4.506243416838688,Low,Public,87.99949186558312
|
78 |
+
10.174094136476343,3.443418101340249,Medium,Public,73.21493108251659
|
79 |
+
9.401985299068265,4.571884839034087,Medium,Private,68.03311834066925
|
80 |
+
10.183521553071005,6.50075979063431,Medium,Public,76.62881394901405
|
81 |
+
6.024862170798214,5.850221742113493,Medium,Private,74.61139162244369
|
82 |
+
9.560656224324976,4.6513478655921405,Low,Public,76.21498179027404
|
83 |
+
10.714225143023492,4.650742295681571,Low,Private,76.06656702709435
|
84 |
+
12.955788089483033,4.678364948782617,High,Public,77.54663726009179
|
85 |
+
8.963459563452705,7.076747983560841,High,Public,79.64847171089932
|
86 |
+
8.383012794213625,5.381935452231553,Low,Private,78.06146014314363
|
87 |
+
8.996485912830927,5.43004164719107,High,Public,77.66739457192861
|
88 |
+
11.830804235404148,6.030283454031842,Low,Public,81.44038199608131
|
89 |
+
10.657502219319369,5.238789159026513,High,Private,79.90639459866581
|
90 |
+
8.940479592465923,4.740957854179297,Medium,Public,75.69661079898786
|
91 |
+
11.026534866226712,4.803650150915022,High,Public,80.20702961491426
|
92 |
+
10.194155098696081,4.928398740585936,Medium,Public,71.85938355317546
|
93 |
+
11.937289981065778,4.962777763490158,Medium,Private,82.30537836044118
|
94 |
+
8.595893812245295,5.72762954363698,Medium,Private,73.5802495976151
|
95 |
+
9.344675706804464,5.051945885807299,Medium,Private,81.47036327236867
|
96 |
+
9.215783693735684,5.73264007721558,High,Public,77.69512071204612
|
97 |
+
7.072970103735763,4.919283419891418,Low,Public,75.70226144642311
|
98 |
+
10.592240554129152,5.07863519031609,Medium,Public,77.29352695848367
|
99 |
+
10.522110544359778,3.0017993154678924,High,Public,74.21835087386411
|
100 |
+
10.010226913284923,5.916327674702481,Medium,Private,86.36620156990523
|
101 |
+
9.530825733249706,5.346488475897993,High,Public,79.26306012242395
|
102 |
+
7.1692585158991715,5.998010109859652,High,Public,69.76295953455913
|
103 |
+
9.158709354469282,2.1037446218063107,High,Public,75.9579078347504
|
104 |
+
9.31457096694646,7.088374704780728,Low,Private,76.59518032392467
|
105 |
+
8.395445461556761,4.860410371844826,High,Public,75.20999716993838
|
106 |
+
9.677428576667982,6.108182816737516,High,Public,87.70050453273926
|
107 |
+
10.808101713629076,3.960094072874469,Low,Public,70.39993259837959
|
108 |
+
13.77237180242106,5.61277390506257,Low,Public,85.08499485360329
|
109 |
+
10.349155625663679,3.9465844368263436,High,Public,78.65954611107702
|
110 |
+
10.515100781445529,4.376231039304195,Low,Private,81.41803979517071
|
111 |
+
9.851108168467665,6.914031353866786,Medium,Public,73.86709383093114
|
112 |
+
6.1624575694019175,4.809317599236653,Medium,Public,61.859940194783725
|
113 |
+
9.946972249101567,5.217432873179723,Low,Public,83.17384056792484
|
114 |
+
10.120460419882052,5.8700677306887545,Low,Public,75.44394815177805
|
115 |
+
14.926484224970572,5.495681887972605,High,Public,84.28507874191348
|
116 |
+
9.615278070437755,5.150418905143576,Low,Public,84.70743286245171
|
117 |
+
10.603094684667225,5.3649610024662255,High,Public,79.6021424466929
|
118 |
+
9.930576460589513,7.403415585238275,Medium,Public,81.34746743744431
|
119 |
+
7.6626439247609355,4.9423812029664145,Low,Public,71.83986242821382
|
120 |
+
12.285645629030041,5.201099046714967,High,Public,76.25302949441668
|
121 |
+
11.503866065373549,6.050654396007612,High,Private,85.46219847173046
|
122 |
+
11.582063894086094,6.105525932957984,Medium,Private,84.36966929723727
|
123 |
+
8.181225090410521,6.187030305560381,Low,Private,76.48645226585877
|
124 |
+
12.805588621872198,5.638730222029185,Medium,Private,83.48765738040687
|
125 |
+
7.196297874415438,3.8569950872695165,High,Public,68.7164901330437
|
126 |
+
11.17371418760054,6.6334315323010955,High,Private,86.8599926213898
|
127 |
+
14.380911251619956,3.853654606981312,Low,Private,83.8358390976814
|
128 |
+
8.018927349738624,5.3026354652390335,High,Public,73.67150270466635
|
129 |
+
8.867404540794457,4.245724149980642,Low,Private,76.75286108097875
|
130 |
+
10.199302730175283,4.935861653407008,Medium,Private,78.84220259386301
|
131 |
+
8.993048691767601,5.32876241030348,Low,Public,84.19062128669262
|
132 |
+
6.898673137867735,5.3213572154502735,Low,Public,62.972548211544364
|
133 |
+
10.137125949612054,5.421920754235285,High,Public,79.90667022241335
|
134 |
+
7.87539257254779,6.613711269058647,High,Private,72.1433231463227
|
135 |
+
10.947184861270363,5.453534301513979,Low,Private,75.36423850103705
|
136 |
+
8.161151531532393,4.755843364709351,High,Public,71.17074707990709
|
137 |
+
13.09986881003508,5.964087168288358,High,Public,85.4192654149115
|
138 |
+
8.433493415327526,6.18947048892464,High,Private,76.64653794365906
|
139 |
+
9.35587696758865,3.7723921848630075,Low,Public,66.78381113407934
|
140 |
+
11.62703443473934,5.597400069849858,High,Private,78.94474982586438
|
141 |
+
7.53827136713209,5.701172742310899,Low,Public,75.03440960511283
|
142 |
+
10.45491986920826,4.702436496640008,High,Public,73.57884222945826
|
143 |
+
12.614285508564857,6.37570681331162,Medium,Private,79.21821171351456
|
144 |
+
6.785033530877545,4.849944412967473,High,Public,59.56432214374158
|
145 |
+
10.36926771706461,5.125576453471543,High,Public,75.11093287245974
|
146 |
+
10.519765588496847,4.82692817571613,Low,Public,72.53177744581382
|
147 |
+
11.56364574355462,5.015579047581467,Low,Private,75.73099564293115
|
148 |
+
7.526098578243836,3.9037249131047984,High,Public,69.5486830097466
|
149 |
+
7.359086773831447,3.5599491174057056,Medium,Public,67.05895284105696
|
150 |
+
11.043883131233795,6.594505063224972,High,Public,76.14833438800852
|
151 |
+
10.593969346466372,4.153038651681673,High,Public,66.83828884428911
|
152 |
+
10.500985700691754,4.008607650543693,Medium,Public,78.52643940293727
|
153 |
+
10.692896418993952,2.846609886396285,Low,Public,79.4198437366247
|
154 |
+
8.639950556843019,4.361038252310498,High,Private,71.8377335971799
|
155 |
+
10.464507394322007,3.6769102065925017,Low,Private,75.11550161626137
|
156 |
+
10.586144946597363,6.642015160136369,Low,Public,81.1493652118299
|
157 |
+
8.571297163947264,6.009817089342635,Medium,Private,84.05123784496975
|
158 |
+
13.731549022289514,4.311849654822276,Medium,Private,76.09233355768359
|
159 |
+
10.947665841823575,7.252435805344309,Low,Public,80.70153676230076
|
160 |
+
7.617393005594703,5.981765486991594,High,Private,65.5975694210132
|
161 |
+
11.31310721726766,4.675168616491377,Low,Private,81.45055965295018
|
162 |
+
8.050636659545358,2.500594285126427,High,Private,69.1323727778403
|
163 |
+
11.574169207484903,7.290942572590742,High,Private,85.0997128385363
|
164 |
+
12.317191158014808,3.610427533236404,Medium,Private,80.8156364288396
|
165 |
+
8.358635363296578,3.354601253043108,High,Private,76.7027138746754
|
166 |
+
11.926752258488644,6.0225704320096405,High,Private,85.57802498783408
|
167 |
+
10.825561853872996,7.439752406339273,Low,Private,86.96329210555535
|
168 |
+
11.64412031998898,6.384272818510518,Medium,Public,77.53697641825902
|
169 |
+
13.793585965307894,5.56390912005236,High,Public,91.89888993557005
|
170 |
+
9.509223767994259,5.594754341665286,High,Private,73.79876535104144
|
171 |
+
8.492527671285021,5.853415558780307,Medium,Private,72.44083096762058
|
172 |
+
8.220971140748954,5.758928589786765,Low,Public,76.43454805294303
|
173 |
+
8.368379430069123,5.281191423969376,Medium,Private,76.1681373113387
|
174 |
+
9.845796581171792,5.104201103951876,Medium,Private,71.98861080710901
|
175 |
+
10.682303949633289,4.937406872100477,High,Public,77.9043217485478
|
176 |
+
10.553381598660039,4.246035411133324,High,Public,84.91699289836744
|
177 |
+
11.654366498072047,4.719324923133226,Low,Public,73.04603757317346
|
178 |
+
10.026003783755813,3.3070431858046145,High,Private,82.43866855128218
|
179 |
+
12.907068154314633,4.90166037320525,High,Private,81.95813461135468
|
180 |
+
9.470686333524087,4.011408892849083,Medium,Public,63.26971183914603
|
181 |
+
15.440338333179238,3.896410682784229,High,Public,79.8916340231203
|
182 |
+
11.251334695530012,5.179894151153478,Low,Private,75.45512292911428
|
183 |
+
8.285684887167434,6.392002286344599,Medium,Public,73.03972669993806
|
184 |
+
7.858215003877776,5.918316606002313,Low,Private,74.07033862363232
|
185 |
+
10.96494483048637,3.4294993963768228,Medium,Private,63.55673810673012
|
186 |
+
9.553074429348298,4.010371863429662,High,Public,76.05572861622936
|
187 |
+
11.428000988184184,5.940771187988216,Medium,Public,76.49170621672627
|
188 |
+
10.94647524914709,4.017512606461691,Medium,Private,84.87183226599201
|
189 |
+
9.854342174686254,4.775366850002976,High,Public,75.52085102065709
|
190 |
+
8.30641256386319,5.550052099024552,High,Public,79.65213413492687
|
191 |
+
6.970305550628271,4.031655545136365,Low,Public,75.53983931853256
|
192 |
+
9.106970095865957,5.10537550614596,Low,Public,81.28836966828194
|
193 |
+
11.712797588646945,3.6659745050817953,High,Private,76.09252322010416
|
194 |
+
10.428187488260408,4.398632356048786,High,Private,74.55832272198738
|
195 |
+
7.508522442576024,5.3197819341425365,High,Private,71.87185128992428
|
196 |
+
10.346361851702364,3.407006266468331,Medium,Private,78.98441998000185
|
197 |
+
10.770634759457673,5.440474737960399,Medium,Public,77.14850527708512
|
198 |
+
8.232285127597734,4.980362201073318,Medium,Public,73.3388011888542
|
199 |
+
10.307450211891055,5.5524899544171475,High,Private,80.31160443710507
|
200 |
+
10.116417436892,5.223914134064661,Low,Private,73.54424644464166
|
201 |
+
7.714059404338753,6.364140429973844,High,Public,70.13378801199269
|
202 |
+
10.715574720696566,5.1252245026479075,Medium,Private,78.32766678834504
|
203 |
+
11.121569052736469,4.570594458874747,Medium,Private,78.96992386854328
|
204 |
+
12.166102486350553,5.122297503032284,High,Public,84.18507862725775
|
205 |
+
12.107604104069805,5.543298029036388,High,Private,85.88958812645198
|
206 |
+
7.244661264085818,5.048860070325705,Medium,Public,66.56729853080195
|
207 |
+
8.124349920169754,5.040591691203883,Medium,Public,69.74916271742751
|
208 |
+
11.03007053441732,4.298008312193245,Medium,Private,73.653392099309
|
209 |
+
11.027571901824418,4.337099082449052,High,Public,85.62302943527952
|
210 |
+
11.030095372612095,3.5973947282003067,Medium,Private,76.85492988651198
|
211 |
+
17.70546298130944,6.749576743186939,Medium,Public,95.61593755362438
|
212 |
+
11.141781021386334,3.7561367646087147,Low,Private,78.20663900975332
|
213 |
+
12.271131280361198,4.307094802204093,High,Private,77.42455469544034
|
214 |
+
11.908003526986406,4.281592734430802,Low,Private,88.05974375034917
|
215 |
+
11.302782502611596,5.8949243769444415,Low,Public,83.50308484473348
|
216 |
+
9.369461510719308,4.705050321707972,Medium,Public,75.9944511528903
|
217 |
+
11.517938440986535,6.247742072673734,High,Private,76.88120788523084
|
218 |
+
8.454349570924856,4.326509375756132,Medium,Public,67.58880074720268
|
219 |
+
9.526362786519982,5.278994162223802,High,Private,75.42667915834683
|
220 |
+
9.029272904341793,4.164652946740243,Low,Private,70.80556626093481
|
221 |
+
10.163748278772646,7.145149127773388,High,Public,75.70799852147262
|
222 |
+
14.629317133347017,3.812401580970487,Medium,Private,83.12244654029199
|
223 |
+
6.265469614816504,5.309820710227028,Medium,Public,69.41353015359411
|
224 |
+
11.372520380749027,5.633776880932098,Medium,Private,81.90463318519754
|
225 |
+
6.774568257620697,5.4137990974514985,High,Public,69.15023780454733
|
226 |
+
9.056136268421133,4.814712341122291,Low,Private,70.29633757522555
|
227 |
+
12.177901193934732,4.870179300266574,Low,Private,76.79646690831309
|
228 |
+
10.128560038190926,5.04381147197911,High,Public,83.88770093048146
|
229 |
+
7.844510444141388,4.852997998371511,Medium,Private,66.70972945403133
|
230 |
+
8.569392581480063,5.96387911679491,Low,Private,77.90051532960884
|
231 |
+
11.35919549786935,7.210523001591351,Medium,Public,86.13190714699772
|
232 |
+
8.539266736565727,4.442508214992166,High,Private,73.13591841644137
|
233 |
+
10.43291717916395,3.630197020420922,Medium,Public,85.15027518098518
|
234 |
+
10.091143679807628,4.911717951648745,Low,Private,80.05845925222688
|
235 |
+
8.696799304788366,7.579709337654318,Low,Public,87.97767785758145
|
236 |
+
14.28788817865065,4.196325432030269,Medium,Private,81.61494449577233
|
237 |
+
11.267838044636022,6.6391168054440675,High,Private,79.32462424797104
|
238 |
+
5.949714826684786,6.677700814076441,Medium,Private,74.55366666788431
|
239 |
+
10.372908629538856,4.4464117582920055,High,Public,76.10810670991971
|
240 |
+
8.676427070463223,5.5689830791852755,Medium,Public,84.13465588416443
|
241 |
+
11.704866669592448,6.628396623128538,Low,Public,85.06083019203238
|
242 |
+
8.4149585231346,4.620872259012513,High,Public,68.5383543355044
|
243 |
+
9.770527117066202,4.796419639897638,High,Private,68.05160165125872
|
244 |
+
11.009974557960915,4.418319086051357,High,Private,78.25623478328254
|
245 |
+
11.731510388340244,3.985243269307133,Low,Public,78.995270032975
|
246 |
+
7.599407185888447,4.350722454933993,Medium,Public,73.29372767071982
|
247 |
+
9.330997528318104,3.7760597332377435,Low,Public,80.37953752527234
|
248 |
+
9.050109377678087,5.034083468259226,High,Private,81.04691047771465
|
249 |
+
8.693341534852577,4.230026768941358,Medium,Public,70.83721806143504
|
250 |
+
13.530908480562193,5.233785911790748,Low,Public,78.4433988197884
|
251 |
+
10.80996342192191,3.444104353796373,High,Private,70.61547769949303
|
252 |
+
7.478232091329909,5.330880232214457,High,Private,69.26877600437462
|
253 |
+
11.835723894109552,5.833528961608924,Low,Public,80.58808349043738
|
254 |
+
14.244312394025266,3.0062643574285444,Low,Private,84.52333201901918
|
255 |
+
12.064930521102294,5.374056569847572,Low,Public,78.18610791249606
|
256 |
+
6.961260068091973,6.227668992473292,High,Private,74.66598348046047
|
257 |
+
9.031531854267497,3.7903589813087946,Low,Public,75.55880290979344
|
258 |
+
12.533822298373245,6.672572386837704,High,Public,85.79322258998249
|
259 |
+
8.584661068762438,5.419019009366045,Low,Public,69.65473566651046
|
260 |
+
10.887638856292456,4.294988144241591,High,Public,80.55689355512709
|
261 |
+
11.549268106858673,4.944230922342294,High,Private,90.9212307410644
|
262 |
+
8.146139056843834,5.558326912521722,Medium,Private,75.85925261703478
|
263 |
+
9.8809492878764,5.076005391412411,Low,Private,62.610531159615064
|
264 |
+
3.517465319861855,5.538755992446369,Low,Private,71.54746248862777
|
265 |
+
7.951224717331421,4.079326406920634,High,Public,68.50770016154686
|
266 |
+
9.49486369721368,5.169360824036078,High,Public,88.07233208295995
|
267 |
+
7.504433636070301,3.58628550309502,High,Private,70.97011278361295
|
268 |
+
13.26482260786327,4.888773938651299,Medium,Private,75.43930265377435
|
269 |
+
7.139717244078735,4.09609235853521,Medium,Private,60.100085576166535
|
270 |
+
9.119911026606033,4.264470057410233,High,Private,82.33523095270681
|
271 |
+
10.261481154572182,6.236093175210438,Low,Private,85.40662700154638
|
272 |
+
12.882546578132231,6.091310120594269,Low,Public,96.03215276598932
|
273 |
+
7.128275697641121,5.609138120907177,Medium,Public,72.72488100451078
|
274 |
+
12.32632750430992,3.907687235327421,Medium,Public,80.23153644471256
|
275 |
+
10.020466122039174,4.683591550305198,High,Private,75.15272560874337
|
276 |
+
8.036982697904097,6.213097699979183,Low,Private,72.10216073948087
|
277 |
+
10.92420694852654,5.141716913639905,High,Private,77.59884149522387
|
278 |
+
10.39811939114694,7.319329539981695,Medium,Private,82.86254975388658
|
279 |
+
8.79956624568241,5.3933178393943715,Low,Public,71.46960531589588
|
280 |
+
10.139604169980037,5.192049116490032,Low,Public,69.7208352803252
|
281 |
+
9.22937280627648,4.690883535797498,Medium,Private,76.29445408632009
|
282 |
+
10.227034690502496,5.133540904782282,High,Public,73.82340274471125
|
283 |
+
11.324261349042093,4.8475301636194565,High,Public,63.72262931881302
|
284 |
+
13.172033632290704,5.708108676688591,Low,Private,87.66906723289937
|
285 |
+
7.524369002346302,5.956702316794584,Medium,Private,68.44349628929935
|
286 |
+
14.266066749312532,4.21401053949205,Low,Private,88.1620965813168
|
287 |
+
6.095824400954996,3.668767046442222,Medium,Private,70.88403990415323
|
288 |
+
9.696429809928834,3.1637946268967037,High,Private,75.55650161781688
|
289 |
+
11.176634412969154,5.507991326912544,Medium,Private,93.0993450180896
|
290 |
+
10.561983735470065,3.8966333938261264,High,Public,76.27865831181744
|
291 |
+
8.754600960358813,2.847109407047517,Medium,Public,70.35166819379596
|
292 |
+
9.58375549928545,5.388578604530964,Low,Private,80.66874101245195
|
293 |
+
9.013998130682335,7.492999517432791,High,Public,78.9902587269449
|
294 |
+
8.821270486111576,4.993929088460106,Low,Private,80.93590229188335
|
295 |
+
11.69920419404205,5.838490774451642,Low,Private,75.81211501188395
|
296 |
+
10.714030971930095,5.081829358547565,High,Private,72.78993374435984
|
297 |
+
8.614180809478691,4.901110346808857,High,Private,78.40907459580175
|
298 |
+
11.799199750866501,5.9190764829877685,Low,Public,76.65295952952164
|
299 |
+
10.614599041753218,4.709725455811348,Low,Private,81.33927929943702
|
300 |
+
11.62572423767792,5.267392314071903,Low,Public,83.00255117751553
|
301 |
+
11.259257683847224,5.321697806196013,Low,Private,77.02862064685014
|
302 |
+
8.342009978155856,4.331909546356951,High,Private,75.01773507701431
|
303 |
+
8.879637919606061,5.992042349536523,High,Private,70.57437713459572
|
304 |
+
11.494587210246523,4.825040243304591,Low,Public,75.22723515413144
|
305 |
+
11.22074053086693,4.244254840507155,Medium,Private,77.62192463043426
|
306 |
+
9.958196812071703,5.536509843570403,Medium,Private,78.61579748486011
|
307 |
+
10.234654766617563,4.101532018208963,High,Private,75.10341487560929
|
308 |
+
12.555329791576849,5.028181157369128,Low,Public,84.81851113857296
|
309 |
+
8.81685722232834,4.990881003355796,Low,Public,77.06236802493183
|
310 |
+
11.094194762340075,6.085895565831578,High,Public,88.96406691516823
|
311 |
+
9.595614695132213,5.474698232879953,Medium,Private,76.92685000385933
|
312 |
+
9.564637593545559,4.974973041254856,Medium,Public,82.56455190489403
|
313 |
+
12.19755370397438,5.817766299200287,Medium,Private,87.72538442121865
|
314 |
+
11.65083269797606,6.390207549017314,High,Public,94.35686313594383
|
315 |
+
11.627019272001277,5.557810306351862,Medium,Public,80.95446390401898
|
316 |
+
12.610957614308658,5.01035261984338,High,Private,80.63565055111745
|
317 |
+
10.042007683265519,3.6881637676498404,High,Public,81.90473212922447
|
318 |
+
11.363905942589927,3.9348863369246274,Low,Private,80.5573042492731
|
319 |
+
9.379466486813088,4.694775295645332,Medium,Private,82.20939200101367
|
320 |
+
10.648332704976884,4.390487797840716,High,Public,81.46639109666233
|
321 |
+
9.73971389126463,4.813028697014554,Medium,Private,66.16516037105362
|
322 |
+
10.193991929985437,5.0566499248971075,Medium,Private,79.85456763247245
|
323 |
+
11.190314050873827,5.529692754356731,Low,Public,82.23124672304019
|
324 |
+
8.363558633533055,4.9295012219409085,Medium,Public,74.78690361229728
|
325 |
+
14.184774551370921,5.486501643826202,Low,Private,85.93808629786422
|
326 |
+
7.987965237000596,5.06447441481012,High,Public,65.61192445724473
|
327 |
+
7.571622774424536,3.0245334336461287,Medium,Private,74.05093028550988
|
328 |
+
12.316221747000135,4.060664606181246,High,Private,82.59228254373961
|
329 |
+
11.583325387925871,4.855912444278385,Medium,Public,85.96030292925643
|
330 |
+
11.24823963410431,3.7903052571588924,High,Public,70.48815953257069
|
331 |
+
11.25669101852856,5.599928729996204,Medium,Public,83.44894757977777
|
332 |
+
9.97550645430617,6.530750832343697,Medium,Public,78.57384246939507
|
333 |
+
8.205491257028337,6.218761851697317,Medium,Private,76.4580653412303
|
334 |
+
10.151609116387453,4.786557129069792,Medium,Private,76.36725575547514
|
335 |
+
8.645676576975777,6.490726136849952,Medium,Public,79.74548199662713
|
336 |
+
11.950239466835502,5.148667457202111,Medium,Public,80.80911451451627
|
337 |
+
9.705885236995723,4.662914028881961,Medium,Private,71.14656689203595
|
338 |
+
8.349005606414977,4.38659733645906,Medium,Private,73.7100343114495
|
339 |
+
9.357228316694012,4.697530313511872,Medium,Private,74.68987941797833
|
340 |
+
10.82586290855125,4.611823181458771,High,Private,78.15249248364624
|
341 |
+
8.87255089439205,5.170416222375488,Low,Public,59.561668742146615
|
342 |
+
8.355559208867136,5.160573981049865,Medium,Private,77.8651598071526
|
343 |
+
10.487374422983825,5.003046020153414,Low,Private,76.90599978946386
|
344 |
+
10.489933142217446,5.43693816974635,Medium,Public,72.39450566896309
|
345 |
+
8.98611364925774,6.190646274786341,Medium,Private,81.8710142949357
|
346 |
+
9.057923388763355,5.949554135441324,High,Private,80.22200661945335
|
347 |
+
10.464099874715272,3.5151020315500965,High,Public,74.56425181538141
|
348 |
+
7.103831317005351,2.446078865098045,Low,Public,62.11593403405768
|
349 |
+
7.1850724512468895,5.934319911239274,High,Public,74.16177844274955
|
350 |
+
8.563111557495128,3.6331213015845787,High,Private,73.19143549030517
|
351 |
+
9.573105696576306,4.775234598107317,Low,Private,80.9307347052725
|
352 |
+
10.621815131196009,3.8298869739406403,High,Public,74.76978314900053
|
353 |
+
12.950712433899104,3.1980195624199803,Medium,Public,86.67306488575316
|
354 |
+
11.71531924640404,5.541462728204788,Low,Private,79.04156756843642
|
355 |
+
9.680122940073145,5.7591551603026465,Low,Public,71.26981969788815
|
356 |
+
9.961967584194623,4.423489599273281,Medium,Public,70.89775578991276
|
357 |
+
7.994941270724382,2.40895770785509,High,Public,69.29126140755332
|
358 |
+
9.96297372801522,4.453755551747517,Low,Private,76.27900823835806
|
359 |
+
9.422682722159724,5.3918040054025465,Low,Private,78.57237715617714
|
360 |
+
10.64543712067618,3.5210884312258712,Medium,Private,76.12063673259895
|
361 |
+
8.345538112895355,5.183359919779155,Medium,Private,79.3527086857811
|
362 |
+
11.038693028482344,4.984690150827005,High,Private,78.24895079089231
|
363 |
+
13.065477826005155,5.579291499601088,Low,Private,74.51851157705488
|
364 |
+
9.782479703086285,5.119580368414153,High,Public,76.63884651646482
|
365 |
+
10.803423444197882,4.026931059118445,High,Private,67.61335003824102
|
366 |
+
11.380287983422225,6.196571501664492,Medium,Private,83.41238812432967
|
367 |
+
9.197559056228327,4.84147042697303,Low,Public,80.94328874784078
|
368 |
+
10.448184963620834,4.972695460490068,Low,Private,73.42429068656418
|
369 |
+
10.02518480156359,4.066732040926804,Low,Private,83.24325109647663
|
370 |
+
10.195352197097664,4.55671774880849,Medium,Public,81.4399372410502
|
371 |
+
8.453980432289066,4.115197285973099,Medium,Public,79.11315757061304
|
372 |
+
10.049020348517885,4.827053940076577,Medium,Public,78.28644088932379
|
373 |
+
10.9959965824909,6.7117084801423434,Medium,Private,77.09573000643621
|
374 |
+
12.902287215590084,3.6280988570686437,Medium,Public,85.15079985585383
|
375 |
+
11.918541652170413,3.386438602123259,Low,Private,75.79373667368402
|
376 |
+
14.306364915023114,6.471170327493608,High,Private,88.9089078518034
|
377 |
+
8.4653048742239,4.7906763228481335,Low,Private,73.25986926870192
|
378 |
+
11.744641273441356,4.330927261340084,High,Private,85.60915272287733
|
379 |
+
10.366684011476703,6.039904687396076,Medium,Public,83.44486008366417
|
380 |
+
14.379605866435345,4.394384460263136,High,Public,84.94795943170354
|
381 |
+
8.383403429289697,6.826009713546936,Low,Private,67.24778546119904
|
382 |
+
8.320556315638449,5.677925871339269,Low,Public,71.98186410563979
|
383 |
+
8.801214709111957,4.5120885918542974,High,Private,77.89167375342745
|
384 |
+
5.752208551380386,7.1573082132655035,Medium,Private,73.64093072789196
|
385 |
+
8.948489956638477,4.3942850769966615,Medium,Private,66.97229785670764
|
386 |
+
8.481734676892604,5.742095372009307,Low,Public,66.54367254945471
|
387 |
+
10.300787572952416,5.299292580406492,High,Private,79.52805976730185
|
388 |
+
10.683511951554319,6.301741289398798,Medium,Private,75.53434999862796
|
389 |
+
13.752341678431772,6.561511196726853,Medium,Public,82.54296784776595
|
390 |
+
11.9008476763721,5.0320041490657665,Low,Public,84.71274022025612
|
391 |
+
8.846192688675194,4.246582129653817,Medium,Public,80.07345247390948
|
392 |
+
8.203170657303284,5.459972142939116,High,Public,70.84278939383375
|
393 |
+
10.983838343013012,4.322284630247894,Low,Private,78.227302093337
|
394 |
+
7.359533585958715,7.013387247526623,Low,Public,74.86791471328556
|
395 |
+
13.662917531708707,5.1365353310827375,High,Private,84.20961753655894
|
396 |
+
12.358880241442574,4.634678448687891,Low,Public,81.87277053669848
|
397 |
+
9.061648695790591,5.184680305864909,Medium,Public,79.68281248957933
|
398 |
+
6.573730941818245,3.6528737104703035,Medium,Public,65.25715071569397
|
399 |
+
12.707744748330825,4.028385961489349,High,Public,73.83168076631534
|
400 |
+
9.770920309494764,6.200413907944426,Medium,Public,87.29251114892497
|
401 |
+
12.475632623946924,4.343105721028604,High,Public,86.913033451887
|
402 |
+
6.811144682411266,3.95308901731806,High,Private,64.03373353455264
|
403 |
+
8.801249954092455,5.536652752527286,Medium,Public,76.58563908085561
|
404 |
+
10.010487399436366,6.1857041546629965,High,Public,84.35304185379778
|
405 |
+
10.093961187529484,5.718953310665067,Medium,Public,82.51157206368677
|
406 |
+
9.099869057041513,5.9960476858058165,Low,Public,79.50387619243818
|
407 |
+
11.245699864694998,4.2432049114305395,Low,Private,78.00507263857709
|
408 |
+
7.864759141234812,3.5781893332581562,High,Public,79.73332190766665
|
409 |
+
9.715241029957413,6.501333651955487,Medium,Public,80.28677690014534
|
410 |
+
10.240591263423799,4.677320161157864,Medium,Public,74.92236647198094
|
411 |
+
11.028877668117499,4.749166983544953,Low,Public,83.53688445578041
|
412 |
+
11.42322975617778,6.328194141849058,Low,Private,82.81904393488624
|
413 |
+
7.750715816324261,5.55623000940066,Medium,Public,75.58476528301762
|
414 |
+
6.931771658528755,5.455887772081607,High,Private,73.06312098313596
|
415 |
+
12.555353643797018,7.165002344901084,Low,Public,88.34208867378022
|
416 |
+
10.664628023959184,4.356481769619857,High,Private,72.97169743535211
|
417 |
+
8.503026926886893,5.927840128081237,High,Private,68.64228211004558
|
418 |
+
13.102303951045046,5.057013124897084,Low,Public,90.96079120560363
|
419 |
+
10.231349268585717,5.268592278863257,High,Public,79.61350976441216
|
420 |
+
12.358594368127653,6.528468426412532,High,Private,88.364127762917
|
421 |
+
10.135036962820218,5.507835756289624,Medium,Private,69.73303145918675
|
422 |
+
14.121495849763974,5.538296079049633,High,Public,91.88385136167491
|
423 |
+
13.510681684886409,6.072507337309802,Medium,Private,83.52269614895869
|
424 |
+
9.502071703041853,4.635047270457115,Low,Private,71.92562347772416
|
425 |
+
11.94314190190871,4.160790332660993,Low,Private,79.81060346464898
|
426 |
+
11.290751899170296,3.955190805978966,Medium,Public,73.62438895459641
|
427 |
+
12.737263115064698,3.0336434106239984,Low,Private,80.01633285493736
|
428 |
+
8.070153078839791,7.056207129940322,High,Private,76.04292511751764
|
429 |
+
11.372102919996879,3.896791634278317,High,Private,83.89137281758065
|
430 |
+
12.116848973699176,4.778746376414228,High,Public,88.96854002271704
|
431 |
+
6.482521027153771,4.723186700111079,Low,Public,77.45740106868207
|
432 |
+
7.63348297466845,5.307406697809101,High,Private,61.90550109906998
|
433 |
+
5.921535644479799,5.815737212539297,Low,Public,65.89084295855211
|
434 |
+
9.461186331110884,5.860473488359745,High,Private,66.33864907564957
|
435 |
+
11.435084511591924,4.416922561431061,Medium,Public,73.52784149315528
|
436 |
+
13.004714104192056,4.832878286213589,Low,Public,80.01115458318533
|
437 |
+
10.148189560839551,5.2825799504883,Low,Private,80.27305712354628
|
438 |
+
13.257231091142584,4.751308873816453,High,Public,81.04175932054714
|
439 |
+
7.239797083570217,6.607345576019993,Low,Public,71.93026217642719
|
440 |
+
6.593235121289691,5.490974951638251,Low,Public,72.33175608603557
|
441 |
+
9.888904602206763,5.734877786049817,Medium,Private,69.83357765744921
|
442 |
+
10.768130897878615,5.662881268667412,Medium,Public,75.83761793393903
|
443 |
+
9.934610503811815,6.173473857485325,High,Public,76.61164866476474
|
444 |
+
5.865115799920247,5.181021558703707,Medium,Private,68.48975399473933
|
445 |
+
9.821759920974424,3.7031680520441537,High,Public,75.5830418711428
|
446 |
+
7.391060998990294,5.399687951752641,High,Public,80.38490287427474
|
447 |
+
11.339345097660077,4.348643106208171,High,Public,83.74333566003789
|
448 |
+
10.733196492193697,4.471383318250243,Low,Public,74.32691783930309
|
449 |
+
8.120240427345289,5.586364018755093,Medium,Public,73.31338158556588
|
450 |
+
8.972266165326612,6.238283071433636,High,Private,75.65476125234306
|
451 |
+
7.881572956222097,5.021271576736062,Medium,Private,70.72429069494457
|
452 |
+
9.874641805453656,5.308833012598964,High,Public,79.07511225891031
|
453 |
+
11.910284641002477,6.702214944635238,Medium,Public,74.57499393410326
|
454 |
+
8.028547907328912,5.240753179488569,Medium,Public,78.32179752392311
|
455 |
+
11.008093031035688,7.601683114180394,High,Private,91.88646468691873
|
456 |
+
8.939484763255118,5.565509645631544,Low,Private,74.87803129159846
|
457 |
+
8.414254335475311,3.239237240844182,Medium,Public,70.24054240662775
|
458 |
+
9.785939280090885,5.753341621104532,Low,Public,78.98711222788192
|
459 |
+
7.929515355161252,5.381158384882777,Low,Private,79.02769817244773
|
460 |
+
8.892701389305635,6.289752754082746,High,Public,75.59856332564917
|
461 |
+
7.6042442148223035,5.6731813512699585,High,Private,78.38798628705823
|
462 |
+
13.929450265832779,4.861544016016226,High,Private,87.59368991795611
|
463 |
+
10.070527103943457,3.775701763710634,Medium,Public,87.61266407347632
|
464 |
+
8.600548984014829,4.7909767427139744,High,Private,72.74425300628913
|
465 |
+
10.427959821468445,4.1494795457907,High,Public,67.20013672301879
|
466 |
+
9.77534390061834,4.419476550195277,Medium,Public,69.01520702310202
|
467 |
+
9.558060800933553,5.58857840442061,Medium,Public,77.5586303954988
|
468 |
+
11.22833340008685,6.669904504351406,High,Public,87.60093402680457
|
469 |
+
11.51501542009461,5.394671529822981,High,Public,77.85949243282008
|
470 |
+
8.938997704778945,3.8041169379648156,High,Public,72.42950856712845
|
471 |
+
8.84836351871064,5.44460266822014,High,Private,79.28784472640938
|
472 |
+
9.449896605696711,6.196631486645651,Low,Private,73.3685774369696
|
473 |
+
5.39615767052883,4.390217095680618,Low,Private,64.3212939886045
|
474 |
+
6.9696178756028955,4.865982833327083,High,Private,65.39957656314512
|
475 |
+
12.733748534889049,5.014688194729197,Low,Public,88.8806698262847
|
476 |
+
13.289935427002568,4.21510168691776,Medium,Private,90.61088744336362
|
477 |
+
9.501927920887244,5.648280433843859,Low,Private,69.57920154507497
|
478 |
+
11.153113926111534,4.8790518513922185,Low,Private,78.91387813437026
|
479 |
+
10.622500309087073,5.419532443907028,High,Public,86.41059415935942
|
480 |
+
16.157761616910477,4.1125078221979,Low,Private,90.47776088522899
|
481 |
+
12.239149822869154,4.56254169967659,Low,Public,78.87591603025771
|
482 |
+
9.744164817038467,5.722381357827771,Low,Private,70.3410910727641
|
483 |
+
8.088919118799149,4.627166847908233,Low,Public,78.88331126857497
|
484 |
+
6.787107359484855,6.726963851233405,Medium,Public,75.4672365705423
|
485 |
+
10.406927271734446,4.600363814385398,High,Private,70.34946832633821
|
486 |
+
8.487298509431394,5.224684725751886,High,Public,80.76107937686558
|
487 |
+
7.155492580804651,5.93259083187309,Medium,Public,65.20785360062958
|
488 |
+
8.706854231514948,3.5816342670669683,Low,Private,71.68372909279329
|
489 |
+
7.8369039927712105,3.239191188019224,Medium,Private,69.20952125071061
|
490 |
+
13.37428327014513,3.474343685429905,Low,Private,90.05198871267429
|
491 |
+
11.763279513898901,6.262584103427182,Medium,Public,81.61520422353782
|
492 |
+
9.984054717366766,4.448141854107553,High,Public,75.31710878439125
|
493 |
+
12.959888277780053,7.558199285668252,High,Public,80.44168375957402
|
494 |
+
10.154736615295237,4.4357524013426115,High,Private,73.76111343731137
|
495 |
+
8.277431597343472,5.184551303197037,Low,Public,70.24521284507928
|
496 |
+
13.046248154539315,6.542109952567875,Low,Public,82.07954940790698
|
497 |
+
11.077820087369318,7.006092888159123,Medium,Public,82.69620213143669
|
498 |
+
7.925507691347088,7.061503576636397,Medium,Public,64.2723888184358
|
499 |
+
9.619322643832783,6.208366230593143,High,Public,71.00004340647078
|
500 |
+
8.248763493230486,6.024062525811463,Low,Public,74.9785264071424
|
501 |
+
7.234400538071328,5.592526949201183,Medium,Public,73.46386491546409
|
502 |
+
11.852355095063283,5.778361076166638,Low,Private,81.20508781115704
|
503 |
+
13.818833280940261,4.448814283671466,Low,Private,84.10691553846291
|
504 |
+
7.202864852361717,4.1818011165776925,High,Private,68.63284831131371
|
505 |
+
11.125938473381142,4.996625542589048,High,Public,79.65429224449754
|
506 |
+
8.698714861756347,4.829815377475072,Low,Public,76.81286389103637
|
507 |
+
9.025749232470607,4.546771950672725,Low,Private,77.00732503438861
|
508 |
+
8.815212151522262,5.696387447456715,Low,Public,72.73021341693712
|
509 |
+
8.272018460640368,5.955305208570511,Low,Private,80.05246843205124
|
510 |
+
10.097043255889654,5.088406886291847,High,Public,78.61224031373366
|
511 |
+
8.338099767177924,6.477530081055262,Medium,Public,82.55356443763758
|
512 |
+
10.540913651559677,3.858310885866495,High,Private,84.0336557882545
|
513 |
+
9.899523781101726,4.8063405407202815,Medium,Private,69.79492476641765
|
514 |
+
9.52210390626718,4.283177679397195,Low,Private,71.1738366372866
|
515 |
+
8.184872675916804,3.13346338292694,Medium,Private,75.13768062607677
|
516 |
+
8.846457338863335,4.917319314157301,High,Private,66.74443154831788
|
517 |
+
11.510782451651512,4.878252491616716,High,Public,87.43982845419495
|
518 |
+
11.001834375248762,6.5134497432421306,Low,Public,80.05443239129741
|
519 |
+
8.044889510402898,5.630811684554778,High,Public,77.41116172318306
|
520 |
+
10.19866461085845,3.975813175670795,Low,Private,75.7790956709192
|
521 |
+
11.502774246743577,6.854092566334188,High,Private,76.32473970407001
|
522 |
+
6.6611894377572565,6.221033695525353,High,Private,77.93165242946242
|
523 |
+
11.08672038475987,5.582097703468616,Low,Private,94.30020247740931
|
524 |
+
8.674752482108307,4.773515901150168,Medium,Private,71.9347374341634
|
525 |
+
11.141197337186318,4.04056076327652,High,Private,76.7314430650612
|
526 |
+
8.473481686914965,4.627793223928947,Medium,Public,60.64315549338805
|
527 |
+
6.390235798670962,6.088748619704186,High,Public,78.67110904417882
|
528 |
+
6.744915124233675,6.884586305934609,High,Public,78.37553632157928
|
529 |
+
10.096169893322765,6.543243528272189,High,Public,71.34028576877925
|
530 |
+
10.519445003442964,4.511150578213726,High,Public,67.4811885639721
|
531 |
+
8.191366749791182,3.880382566710989,Medium,Private,62.457150933822525
|
532 |
+
11.277184917554749,5.1408862469112035,Medium,Private,83.49154483234038
|
533 |
+
6.67695987546208,3.231560634437148,High,Private,71.92621780065029
|
534 |
+
9.867840402705367,5.323167714184781,High,Private,73.10376265484396
|
535 |
+
7.577967600475087,4.852397413701332,Medium,Private,74.93083147841615
|
536 |
+
8.696327784395681,4.533963498131758,Low,Private,74.98798642094475
|
537 |
+
10.094797342632829,3.4052969327332043,Low,Public,73.46119155009437
|
538 |
+
8.279173269432095,5.513600106569717,Medium,Private,79.67973083214525
|
539 |
+
9.23088891154035,4.467299160338088,High,Private,69.13804855744763
|
540 |
+
12.012585618428881,3.8300831642897117,High,Private,85.50799205126181
|
541 |
+
8.846216260953703,2.1277377867183507,Low,Private,67.24143143191317
|
542 |
+
11.671384224130284,4.972485094949364,Low,Public,89.70917950051984
|
543 |
+
7.740586290684764,6.772251596669744,Medium,Public,70.61301985896681
|
544 |
+
11.059608355830566,6.66125921454787,Low,Private,80.35324514048229
|
545 |
+
12.8831372413158,4.542903743413728,Medium,Public,82.87356578546395
|
546 |
+
5.056710999745421,4.39778793963822,Low,Public,66.97169664806059
|
547 |
+
8.406209489059046,5.468774256075024,Low,Private,77.4697228653211
|
548 |
+
11.15414425436108,4.001614560811708,Low,Private,71.88229825870634
|
549 |
+
9.593909227914015,5.301791899819466,High,Private,76.95394426432405
|
550 |
+
10.742291746742618,5.76608027838469,High,Private,80.82436244307587
|
551 |
+
8.79202962656836,6.226933223884307,High,Private,74.28576796882254
|
552 |
+
10.1731795749458,4.899845923520047,Low,Public,79.71642112246876
|
553 |
+
9.68864552921584,4.796326249823034,Medium,Private,69.83993086572733
|
554 |
+
12.335564123319614,4.122017413243439,Medium,Private,79.22188492125098
|
555 |
+
10.508841686602427,4.17311964596014,Medium,Private,73.51144222012672
|
556 |
+
10.675205324150404,4.773521108098473,Medium,Public,76.89353766270342
|
557 |
+
9.176246067755065,5.367365506816178,High,Private,83.63871769534042
|
558 |
+
9.024787551855013,5.913584626233249,High,Private,84.25223067384691
|
559 |
+
9.134883624360759,4.196821049319655,Low,Private,80.9170811256071
|
560 |
+
10.788904284756594,6.4926885689595215,Low,Public,79.00555502254822
|
561 |
+
9.158031038359475,4.728876398957431,Medium,Private,79.94746805915614
|
562 |
+
10.579549713792826,4.978632705651564,High,Private,79.18885912069203
|
563 |
+
14.150801597290878,4.252788320852177,Medium,Private,84.90873419337507
|
564 |
+
11.742249406863385,2.5757597397270584,Low,Public,76.96133168522152
|
565 |
+
9.347952935664317,5.884045396361049,Low,Private,86.31406325276583
|
566 |
+
12.402427844327889,5.736843897092434,Medium,Private,90.23931456207676
|
567 |
+
9.183849253956897,4.718672442881787,Low,Private,79.17018926447196
|
568 |
+
5.923750929644292,5.066990717224872,Medium,Public,68.59648310757277
|
569 |
+
7.983827378165191,5.5159392177041,High,Private,74.43936737415933
|
570 |
+
6.258416157948289,3.4374541431134573,Medium,Private,70.32584655325543
|
571 |
+
9.296973031917382,4.470947322653705,High,Private,72.69232248854522
|
572 |
+
10.036836758379103,5.794264679621827,High,Public,83.45664911601347
|
573 |
+
13.352874624550566,3.7457105768307506,Low,Public,77.71808955713273
|
574 |
+
10.653854747528325,5.293557932166629,Low,Private,81.20726483329362
|
575 |
+
9.561798942382271,3.643418195376595,Low,Public,63.140147428866285
|
576 |
+
11.658811162366979,5.466429982433526,Medium,Public,78.7641746653736
|
577 |
+
5.57772938198423,4.964358517702516,Medium,Public,78.99038220511946
|
578 |
+
10.471229116217131,3.3848681840944908,Low,Private,81.20071798053556
|
579 |
+
11.541730387773933,6.164739354457171,Medium,Private,90.00791205690624
|
580 |
+
7.0428275084403165,4.265408423201951,Low,Public,67.14722931491166
|
581 |
+
12.287508086413858,4.189747562985619,Medium,Public,81.37850170130832
|
582 |
+
10.676992814988829,5.2005691972309425,High,Public,85.56480069129559
|
583 |
+
9.169424172198397,6.148637349428715,High,Private,80.72727904206675
|
584 |
+
11.26556373221257,3.984178181021603,Low,Private,81.05022412274914
|
585 |
+
14.541385715608792,5.061679849847971,Medium,Public,94.82820105763986
|
586 |
+
10.363732510116991,5.428816500330573,Medium,Public,73.6684149426602
|
587 |
+
10.496441172600672,5.693105607263144,High,Public,78.61135055886854
|
588 |
+
9.081278200919511,5.176441555277226,High,Public,67.15679092140809
|
589 |
+
8.300311261070416,4.632972160916678,Low,Private,68.07455050383784
|
590 |
+
11.66067163308849,4.17240977935128,High,Public,84.95088489609876
|
591 |
+
8.287832348182265,5.0861438830463745,Low,Private,69.61813418997346
|
592 |
+
10.143132474438785,3.927860988014393,High,Public,79.85567770152464
|
593 |
+
9.044685106469768,2.0786495165053926,High,Public,79.49976969753803
|
594 |
+
10.957959651492784,5.4365598041576675,Medium,Public,79.18106813426238
|
595 |
+
10.667324210573897,5.903934841086195,High,Public,82.78986788177615
|
596 |
+
12.075079888515798,2.6370675644581616,High,Public,81.82232177143345
|
597 |
+
8.979967202290506,3.9902692952900622,Low,Private,67.8378630321833
|
598 |
+
9.460250129413257,5.619154261005198,High,Public,74.26696992382907
|
599 |
+
8.042472568435386,7.057495469711698,Low,Public,71.65288228307931
|
600 |
+
9.111413479847776,5.020793707675408,High,Public,73.07040197079353
|
601 |
+
10.754600986089704,4.271997058770865,High,Private,81.4977510507412
|
602 |
+
11.513977233290703,4.81710355889516,Low,Private,77.27762615021268
|
603 |
+
8.155669351644748,6.374876419148185,Medium,Private,75.69788719735269
|
604 |
+
11.73921184021132,4.354035820875465,Low,Private,74.79716932707966
|
605 |
+
12.711275717609901,4.200807993349747,Low,Public,87.85587424791294
|
606 |
+
10.826869806447402,4.517256477817968,Low,Public,75.70407437850507
|
607 |
+
13.753591625116131,4.046671391741287,Medium,Public,91.19209309739395
|
608 |
+
8.452421601792853,5.122670315994796,High,Private,74.8934941468247
|
609 |
+
7.510690593377166,6.62467845846956,Low,Public,67.92340098464327
|
610 |
+
6.442559502191443,5.323079273116029,Low,Public,70.0146041813288
|
611 |
+
12.992088622978367,4.7476464978737125,Medium,Private,77.79176285543649
|
612 |
+
11.308731312708115,4.708188731084456,Low,Private,82.62649260893369
|
613 |
+
9.88883065817909,3.436809191303343,High,Private,73.62606119167886
|
614 |
+
10.55993725263964,5.883109777147159,Medium,Public,80.7605904461368
|
615 |
+
7.7490219054032465,4.922162802987198,High,Public,71.03452246505447
|
616 |
+
14.891503959233653,4.819520086409375,High,Private,84.23557652876475
|
617 |
+
10.258442363950454,8.19310756784486,Medium,Public,77.93843603159006
|
618 |
+
10.218789589209786,5.298752908365686,Low,Private,82.56682322507574
|
619 |
+
11.451533247797384,4.248208949904816,High,Private,78.4096144114535
|
620 |
+
10.962018463473427,4.573642403794457,High,Public,77.21104606414663
|
621 |
+
10.447768048558263,6.148445709196755,High,Private,79.12713516870086
|
622 |
+
8.419051089109375,5.113270410537765,High,Private,71.11369882659652
|
623 |
+
10.942936714271992,3.561722021682633,Medium,Private,75.67169932718781
|
624 |
+
13.764048992950068,5.919228934575203,Medium,Public,87.43509046123037
|
625 |
+
12.690840092309955,4.331855914461006,High,Private,76.7087790610276
|
626 |
+
13.186373253278795,6.873297776217545,Low,Private,82.23593320548787
|
627 |
+
8.97756864713763,6.0800480657146485,High,Private,83.9666298609606
|
628 |
+
8.020790359482838,4.552678104341249,Low,Private,71.05681521147976
|
629 |
+
9.748426159800704,6.281016361735113,High,Private,76.16057281758403
|
630 |
+
10.11144982457739,5.067855510527403,Low,Private,71.10532488714951
|
631 |
+
12.188383036941897,5.8527736751222195,High,Private,87.40931477393833
|
632 |
+
6.615070740570362,5.484732817294475,Low,Public,66.17761163952822
|
633 |
+
13.059100638921228,4.153643345009127,High,Public,82.27220299054886
|
634 |
+
9.683984202842021,4.356450167682538,High,Private,67.3617183106249
|
635 |
+
9.14623786010516,6.029960817581161,Low,Public,81.42828094840264
|
636 |
+
7.975791249479665,4.665224667893459,Medium,Public,78.83370071889242
|
637 |
+
6.690286656268461,4.596351536758004,Low,Private,72.697702345064
|
638 |
+
11.64634116792383,4.044877420650647,Medium,Public,71.68759780979714
|
639 |
+
10.146635934376807,5.423599065593461,High,Private,82.62248118668995
|
640 |
+
7.420078200517892,7.062524919243145,Low,Private,71.29719878169874
|
641 |
+
7.409842455872768,3.932467085745727,Low,Private,64.86352614675606
|
642 |
+
9.328430601419743,5.024219456672758,Low,Private,74.27588515906035
|
643 |
+
13.338043050578786,6.412220555415429,Medium,Private,84.27757856754828
|
644 |
+
9.48081729727279,4.9203586087542845,Medium,Public,73.81692925177558
|
645 |
+
6.993714093763788,5.452371796249734,High,Private,78.53435712142564
|
646 |
+
9.50851387182811,3.9376064718531203,Medium,Public,84.98256743364905
|
647 |
+
9.45455286050466,5.428307097207648,Medium,Private,70.20588368007347
|
648 |
+
4.606226714116857,4.812855734995451,High,Private,62.98757505910394
|
649 |
+
9.891410266964376,5.985729995679863,Low,Private,79.47515720767333
|
650 |
+
9.538130939582643,6.187386051564751,Low,Public,76.41375850182628
|
651 |
+
11.392412729626837,7.589563642018614,High,Private,86.16616466867418
|
652 |
+
13.69791218989069,5.579633173213575,Low,Private,90.34520091933071
|
653 |
+
12.253130059095515,5.325796316582166,Medium,Private,86.24178430441448
|
654 |
+
9.462222618890339,5.194384301577336,High,Private,79.76468952939584
|
655 |
+
7.786948182516659,4.6468337071893115,High,Public,80.8396245304159
|
656 |
+
15.14671960649972,5.338483843461691,High,Private,80.35642856361143
|
657 |
+
10.118436868028976,4.704598589860971,Low,Public,84.69431558062249
|
658 |
+
10.027858583825893,5.168460976269614,High,Private,75.74345046160748
|
659 |
+
9.951749825779993,6.31759753560796,High,Public,83.30552205835006
|
660 |
+
10.39616952153571,3.9934574347203586,High,Public,71.63419405375474
|
661 |
+
9.711279176152114,6.139878561129045,Low,Private,74.08989423453141
|
662 |
+
8.852675986239527,6.3171150673398655,Medium,Private,80.491611030899
|
663 |
+
8.906282117519215,4.881931473008922,High,Public,65.49181133080258
|
664 |
+
9.934493459568003,2.8781451009914054,Medium,Private,79.13341496206468
|
665 |
+
8.91315045773247,4.392178012905568,Low,Private,80.71438544653124
|
666 |
+
8.57430843464567,6.296994546561109,Low,Private,77.4830329582778
|
667 |
+
10.212860455383794,4.977131937701118,Low,Public,78.60022399521205
|
668 |
+
9.49004556515829,4.0006977677050894,Low,Private,70.81295705733194
|
669 |
+
13.007985977165378,4.4952250787717825,Medium,Private,86.13105658450678
|
670 |
+
4.698060383213976,5.840620026504553,High,Private,69.89537703822191
|
671 |
+
12.183013703844924,5.546733568201379,Low,Public,78.07860077987279
|
672 |
+
12.492170384995259,4.761067900830306,Low,Private,80.16854287079502
|
673 |
+
5.853219535183701,4.6331755881853125,Medium,Public,67.85952532916636
|
674 |
+
9.314624811839302,4.608241850956178,Medium,Private,73.57760141751693
|
675 |
+
9.257118268008417,4.077589815501497,Low,Public,67.8160641925192
|
676 |
+
7.184976610564357,6.615375689469442,High,Private,77.78811829825234
|
677 |
+
8.44436662481825,4.6776795307886605,High,Private,72.68770026631002
|
678 |
+
7.7788483090683425,6.217158520825496,Low,Public,74.96025827884148
|
679 |
+
13.504540886847245,6.521316051922451,Medium,Public,83.89972291459102
|
680 |
+
11.871356786294923,5.9983108981479605,Low,Public,85.11653728967174
|
681 |
+
12.543110189988317,4.5683796872819515,Low,Private,82.9322708328066
|
682 |
+
11.443344128086471,5.40373009033661,Medium,Private,87.05620533732996
|
683 |
+
7.741896457565542,4.975804393609084,Low,Private,75.35049307023964
|
684 |
+
8.950959467440452,4.096298139269872,Low,Private,73.03294413460016
|
685 |
+
10.978749122455836,5.324359281721603,Low,Public,76.24545307998129
|
686 |
+
7.555744382216109,3.8209602088401975,Medium,Private,75.17269249727575
|
687 |
+
11.425996860344776,6.1876793884615635,High,Private,80.7206323094496
|
688 |
+
9.51934920368373,4.535382702701607,High,Private,74.98318462843878
|
689 |
+
9.250358384900805,5.201159655872839,Medium,Private,75.4425982221475
|
690 |
+
11.421919936406983,5.283287871546142,High,Private,78.76073312684753
|
691 |
+
10.88852662297208,4.741095026295922,Low,Private,78.11600081423869
|
692 |
+
9.27806766836184,5.586693801475275,High,Private,86.56721385259948
|
693 |
+
12.318659606728495,4.525096343377475,Low,Public,76.18585345788475
|
694 |
+
7.837873344800205,5.871297297457244,High,Private,70.41017158197823
|
695 |
+
11.231871213888528,3.6540203197532515,Medium,Private,84.86639499817818
|
696 |
+
11.186202515936767,5.126379579546412,High,Private,78.18795247871779
|
697 |
+
9.380907121372019,6.938928999058285,High,Private,76.91710132095699
|
698 |
+
10.652266044484236,3.9996686753944255,High,Public,82.47835438592728
|
699 |
+
7.497772847229394,4.322255029475149,Medium,Public,64.37795015226274
|
700 |
+
11.848054038413801,5.513907849658915,High,Public,86.074241997583
|
701 |
+
9.630195727114021,5.179581781939598,Medium,Public,77.25784627374516
|
702 |
+
8.95455395896192,5.350630099273559,Low,Public,78.25906372099408
|
703 |
+
12.098018451673779,5.489187130147664,High,Public,84.65997237482969
|
704 |
+
8.59131261891448,5.63472146856177,Medium,Private,72.49479450825842
|
705 |
+
7.183077407272881,6.109699841956223,Medium,Public,78.78662347822963
|
706 |
+
6.8867416529521925,5.409818656928513,High,Public,69.1375227339068
|
707 |
+
11.21201990269128,4.7587423453814575,Low,Private,81.60639841844846
|
708 |
+
7.439141295007434,5.672573701202731,Medium,Public,79.57576556946465
|
709 |
+
13.50958836396873,6.899881934895216,High,Private,94.26350812951263
|
710 |
+
5.836141184232556,4.867366253617426,Low,Private,67.63770006918779
|
711 |
+
13.392912736580076,4.025470695329359,Medium,Public,85.13970788735924
|
712 |
+
10.422034934405238,6.107080669286972,Medium,Private,78.20724692172284
|
713 |
+
9.806573776259215,4.879618836046402,Low,Public,89.4515569429319
|
714 |
+
8.910161826382101,2.827330453985964,Low,Public,75.96194936311224
|
715 |
+
10.798272228704143,5.847421677150422,High,Private,80.7879691523186
|
716 |
+
9.924730595150303,4.464671814046078,High,Private,78.10927977865843
|
717 |
+
12.206603764033042,4.909466717695839,Medium,Public,83.81293468184018
|
718 |
+
10.228455297324079,5.3319803305690145,Low,Private,80.63859879441908
|
719 |
+
10.300603522923755,5.190499679779067,Low,Private,77.18696547728331
|
720 |
+
9.272775575572288,5.70945181713904,Low,Public,75.13809238067726
|
721 |
+
9.886108752557865,4.564513629430157,Medium,Public,75.85972114062851
|
722 |
+
10.615603537784118,5.513105798176083,Low,Private,73.80240724759632
|
723 |
+
6.579663214686748,4.740453322416083,Low,Public,73.86137844786992
|
724 |
+
7.303629155788574,5.738810480887503,Medium,Public,69.20694881966352
|
725 |
+
11.48652818804543,5.615367484013054,High,Public,76.09635720718686
|
726 |
+
10.341730876255884,4.064561297955405,High,Public,76.70853136662413
|
727 |
+
9.632033327294641,6.085982116175032,Medium,Public,77.7424266804024
|
728 |
+
10.036867866130786,4.464036554885952,Low,Public,78.01796294353544
|
729 |
+
10.695163410723342,5.808057798281145,Medium,Public,77.26138667188634
|
730 |
+
8.920480639381276,5.367287313401341,High,Private,73.9291794577097
|
731 |
+
8.443390549195374,6.838183676795122,High,Public,80.60465413902588
|
732 |
+
10.391690510195364,4.776534017628148,Medium,Private,81.10426804930891
|
733 |
+
8.043254444769936,4.650683203881803,Medium,Private,76.01015892624596
|
734 |
+
10.816505511428943,4.980580394112267,High,Private,79.27371967578371
|
735 |
+
6.594832791524319,4.696820217165724,Medium,Public,71.0795090254586
|
736 |
+
12.058311274651288,5.79994190194205,High,Public,83.96872984076816
|
737 |
+
10.945194964826086,3.3836893915634123,Medium,Private,73.68571531795241
|
738 |
+
10.51205946862775,3.9463175808536457,Medium,Public,84.31382830926648
|
739 |
+
11.965381967891028,3.932197078420807,Medium,Private,86.48699971585175
|
740 |
+
13.330948888925153,5.9503075919735755,High,Public,89.5585871208155
|
741 |
+
12.028740130036262,6.710613372581925,Medium,Public,81.99409238693742
|
742 |
+
6.318251537336709,4.8955507814625285,High,Private,72.67554983661796
|
743 |
+
7.4408460665285965,4.83117827680999,High,Private,77.02030923712104
|
744 |
+
8.750362844608642,5.070052163113181,Medium,Private,68.98981942528134
|
745 |
+
10.052182100421668,6.161878302608191,Medium,Public,87.81974046324811
|
746 |
+
11.035318040938247,4.072646865809426,High,Private,69.48266832481167
|
747 |
+
8.548512373693068,5.2383689827347855,High,Public,78.4776073200337
|
748 |
+
10.373533528954157,5.975197629782429,Low,Public,86.9762750338443
|
749 |
+
8.489234135293355,5.5010941699398455,High,Public,74.66479220536134
|
750 |
+
8.776964394016103,5.189581616598743,Low,Private,74.58597921381495
|
751 |
+
7.186677806303555,6.001046092567141,Low,Private,76.71322871469245
|
752 |
+
8.153533507778192,2.296767707000763,Low,Private,79.01162803572689
|
753 |
+
7.296630788767333,5.677875319530908,Medium,Public,71.7047363671242
|
754 |
+
8.048253494045335,4.345924316872576,Medium,Public,68.7106175040698
|
755 |
+
12.10728359321568,3.169367103143543,High,Private,76.84974056310672
|
756 |
+
8.101202222336115,5.511202599524981,Low,Public,66.7066383825286
|
757 |
+
15.26476412967478,6.373658545162957,Medium,Public,94.57880557066977
|
758 |
+
10.986635801761778,4.862551485372308,Medium,Private,76.69668356801272
|
759 |
+
10.369672247389747,5.9528745472029385,Medium,Private,78.62696054525611
|
760 |
+
8.283284439637573,6.612278257988645,Medium,Private,81.03309192335314
|
761 |
+
11.400619758817983,6.314914453584048,Medium,Public,80.38905957596279
|
762 |
+
8.848724347524453,6.639964529371393,Low,Public,81.45284746604798
|
763 |
+
10.244019629290722,5.7421274910718925,Low,Public,73.91078797610784
|
764 |
+
15.12016907653759,5.075433638901592,Low,Public,92.64708575304756
|
765 |
+
9.8078802005507,3.3980341881028315,Low,Public,76.42233678958299
|
766 |
+
12.298546652571352,4.753937511870052,High,Private,81.28510441884944
|
767 |
+
8.593647149748225,4.156753404683808,Low,Public,74.07689184624114
|
768 |
+
9.930023019006077,7.170942717497814,Medium,Public,74.18265364099518
|
769 |
+
13.54160127127102,4.824114187567663,Low,Public,84.85937754362838
|
770 |
+
8.746065884424466,5.123204806784486,High,Private,74.46859005224265
|
771 |
+
13.624897115993857,5.551485376040468,Medium,Private,79.27985924386061
|
772 |
+
11.415503870910952,5.043602446372454,Low,Private,77.04103427684787
|
773 |
+
8.875066448211465,6.695051040430753,Low,Public,77.14648316730646
|
774 |
+
11.264815478111043,4.377350638265548,Low,Private,76.05919598888121
|
775 |
+
11.94510889925346,5.194607455237243,Medium,Private,69.6215594561107
|
776 |
+
11.243619924434393,4.257529414301952,Medium,Private,84.25547166836604
|
777 |
+
6.859550560219088,3.6799774887792944,High,Public,68.52861797815879
|
778 |
+
8.545725648350263,4.388230909094714,Medium,Private,62.30404661415987
|
779 |
+
9.50496272889699,4.962963202951588,Medium,Public,87.12918496061833
|
780 |
+
9.851133141799908,4.57069777548274,Low,Public,82.76527460338986
|
781 |
+
11.241344195101355,4.307579020126144,Medium,Private,77.19914666505025
|
782 |
+
10.355402001866508,3.593682536328166,Low,Public,71.88884035139176
|
783 |
+
7.3293112825797975,4.9168944273713056,Low,Private,74.04992026001916
|
784 |
+
10.760395702011927,3.4952796259689975,Medium,Public,77.77534491739806
|
785 |
+
11.221171490567647,5.760055963689602,High,Private,83.79287655067156
|
786 |
+
11.119580895862079,5.082439752934503,High,Private,75.66472005485068
|
787 |
+
12.161561451109243,3.5424485005350697,Medium,Private,85.12565575787926
|
788 |
+
11.667844309097807,4.6907909229501366,High,Public,78.00456438674308
|
789 |
+
10.918360158456874,4.247843594409417,Low,Private,81.61231445180573
|
790 |
+
9.859668577082706,5.31917451041918,Low,Public,73.71340261750908
|
791 |
+
6.678078132968017,6.340450446023144,Medium,Private,86.10755186373818
|
792 |
+
10.859236438265171,3.1248275303542554,High,Public,79.88333091688801
|
793 |
+
10.415375374326223,5.115026079147417,High,Public,92.92445125024182
|
794 |
+
10.543157674390747,4.83986720443336,Low,Private,82.48412603706177
|
795 |
+
7.446502848359382,5.671340076450465,Medium,Private,70.59269402705104
|
796 |
+
7.837886919183475,5.213196628237689,High,Private,77.64247274866946
|
797 |
+
12.106305706665808,4.248030668261181,Medium,Private,85.08754650668946
|
798 |
+
9.92088969228669,4.680946064333382,High,Private,78.72981920356227
|
799 |
+
11.363001394745249,4.203974143196511,Low,Public,82.18392636444554
|
800 |
+
10.056636752260923,6.076007138598598,Low,Private,83.0359198594581
|
801 |
+
10.059512278991491,5.021311648671942,Medium,Private,72.68258697716219
|
802 |
+
11.876567611951995,6.901190685753008,Low,Public,78.78062922888374
|
803 |
+
8.967910543565253,4.9393391861853795,High,Private,71.3737448733364
|
804 |
+
10.192241553881967,4.291593233089538,Low,Private,77.4595849292508
|
805 |
+
9.075449422589916,3.486285607387173,Medium,Public,72.26447139889349
|
806 |
+
9.131007545135368,3.1968603237440547,Medium,Private,68.17945274627208
|
807 |
+
9.381655753062722,3.4158640569945105,Medium,Private,77.76367864421545
|
808 |
+
10.444267543267426,5.267126651011483,High,Private,80.20444916373448
|
809 |
+
9.042502756673045,5.508725023229934,High,Public,77.19273800504526
|
810 |
+
12.511512251147042,3.418809297967867,High,Private,78.8612919233762
|
811 |
+
8.210785395560992,5.895038314076116,High,Public,73.37298775040829
|
812 |
+
9.626256711677282,4.516938948316561,High,Private,71.82126603678452
|
813 |
+
9.12053788345165,5.146793011985549,Low,Public,80.6299667475683
|
814 |
+
12.893955768707466,6.612220628255424,High,Private,82.18877807153213
|
815 |
+
10.39310955302315,5.896839315865532,High,Public,80.67843041330867
|
816 |
+
12.06368907893727,4.731469352094168,Low,Public,89.53463416737596
|
817 |
+
7.028879253926056,4.1088077745847125,High,Public,68.55968144628243
|
818 |
+
10.534100531738517,2.848184598670695,Medium,Public,80.14398866507905
|
819 |
+
11.779261591246874,4.28084668075758,High,Public,82.82333828380847
|
820 |
+
10.16456797855085,4.788869761172306,High,Public,79.06977841516542
|
821 |
+
12.130960750130702,4.012820472848816,Medium,Private,82.4328944190584
|
822 |
+
8.965423099799256,4.8687430329076955,Medium,Private,74.7594279026054
|
823 |
+
12.81869488037116,5.076851882958768,Medium,Public,83.42654742914767
|
824 |
+
14.5977962472385,4.775144018191922,Medium,Public,83.61773878063626
|
825 |
+
9.274322879120682,4.349997419176541,High,Public,75.28092749267662
|
826 |
+
9.108994957198457,5.168654671966728,Medium,Public,73.04484194342952
|
827 |
+
12.906768954235401,5.441940649569409,Medium,Private,89.0047243482079
|
828 |
+
13.159144291461425,3.909600922557154,Low,Public,73.68520282336635
|
829 |
+
8.954279945693463,6.41093237974627,Medium,Private,79.93022491663375
|
830 |
+
9.159626365808286,4.901411867715738,Medium,Private,77.35430404030159
|
831 |
+
9.436430782278986,5.018849622944848,Low,Public,77.63121581985541
|
832 |
+
7.3110989779314455,5.708214423256557,Low,Public,78.30701433507517
|
833 |
+
8.162696107031604,5.233216131263502,High,Private,72.74459846074373
|
834 |
+
7.991718466495865,5.953136633913245,Low,Private,76.19031110672185
|
835 |
+
8.464404869791743,5.287124165201824,High,Public,66.27678654645396
|
836 |
+
9.930630225226437,4.387562625359562,Low,Public,74.3660199778161
|
837 |
+
10.468429465073042,5.361503525206102,Low,Public,83.25779637383137
|
838 |
+
13.101000985628154,3.856273792679338,High,Private,75.29060398259291
|
839 |
+
8.003291918522418,5.108559705776523,Low,Private,74.46814023096336
|
840 |
+
11.968644796953168,4.966770009355539,High,Public,77.99985924852066
|
841 |
+
9.572022311548983,4.79188314272746,Low,Private,75.77693447833049
|
842 |
+
9.901072580695134,4.871462488561069,Medium,Private,74.0209514504239
|
843 |
+
11.349638984333208,3.1181509545023607,Low,Public,71.14052637895281
|
844 |
+
7.754555956875403,4.4512750922590385,High,Public,74.11604112693836
|
845 |
+
10.764819492368101,5.092844901141115,Low,Private,78.92092252632415
|
846 |
+
10.332904416426112,5.1598563919974,High,Private,81.46873901928431
|
847 |
+
10.984902528016299,3.9723248590740416,High,Public,81.90048188463797
|
848 |
+
10.578337287815636,6.2657078408647005,Medium,Public,86.6815300223734
|
849 |
+
14.91060027982179,4.1338251517855,Medium,Public,86.68937201453515
|
850 |
+
8.724520031497368,5.969457200526737,High,Public,82.74633098680088
|
851 |
+
8.938006089996364,5.427194361871685,Low,Public,71.13793118584549
|
852 |
+
8.753718947150471,4.353772690156197,High,Private,78.5828960756759
|
853 |
+
8.88904576167949,6.775310892609512,High,Private,80.23800994391252
|
854 |
+
8.725225745386965,3.8063631682435197,Low,Private,74.32545950571314
|
855 |
+
12.378033062215103,5.919154173610002,Low,Private,85.85845855566079
|
856 |
+
12.84100849597971,6.000582318042978,Low,Public,79.87919607734656
|
857 |
+
8.858507412501048,4.329379790923355,Low,Public,68.22735046176201
|
858 |
+
8.335288853791543,6.392465300064329,High,Public,84.80841658806456
|
859 |
+
10.942831112772808,4.749953485401047,High,Private,78.16698156266084
|
860 |
+
8.895553911438057,5.288693629279857,Medium,Private,74.8091811577624
|
861 |
+
11.265863635511021,5.260321838771221,High,Private,74.91066265797242
|
862 |
+
10.405846041702599,4.865691258529452,Low,Public,70.70133744371519
|
863 |
+
6.968511770005536,5.8108082731597825,Medium,Private,72.21164155568933
|
864 |
+
13.095010402660122,5.793488720552213,Low,Public,83.17231427860493
|
865 |
+
13.591755346191043,3.2514680267618195,High,Public,82.06938451623708
|
866 |
+
8.774422619031228,6.304340242878153,Medium,Public,77.44112530429055
|
867 |
+
9.224596880128031,3.3375080119643257,Low,Public,85.90276118450602
|
868 |
+
10.57173078144981,6.032546204341657,Medium,Public,78.49938096571732
|
869 |
+
10.668913579974049,6.126705136827583,Medium,Public,88.4275854049067
|
870 |
+
11.31708854534566,3.9090335697848015,High,Private,86.70784852059325
|
871 |
+
14.0204090775327,4.589185712045415,Low,Public,88.69271814949344
|
872 |
+
9.6461055450119,3.8942953315140643,Medium,Public,73.95802242454934
|
873 |
+
8.403405510923093,4.785078969711265,Medium,Private,67.17706779574944
|
874 |
+
7.241361543970946,4.691965715814259,Low,Public,73.91039104833283
|
875 |
+
8.538139920116162,5.7796605322693395,Medium,Private,80.83983103714107
|
876 |
+
9.933746054252406,6.310308746119608,Medium,Private,88.07139254909916
|
877 |
+
13.589115727035576,6.395683810789956,Medium,Private,82.02196739902217
|
878 |
+
8.964777401927657,4.437831973421698,Low,Public,73.36867081806152
|
879 |
+
10.447575903277796,4.790778126255472,Low,Public,73.36199604382456
|
880 |
+
9.967154207854495,3.31656180777905,Low,Private,71.74176116519294
|
881 |
+
12.376786546896168,4.194129933503811,Low,Public,84.36670566522619
|
882 |
+
15.053864851747242,5.9648516330974575,High,Public,85.658483306312
|
883 |
+
8.938262454159359,6.615582823548232,Medium,Private,73.678543257056
|
884 |
+
9.021121114963556,3.765651128120078,Low,Private,77.67569824071859
|
885 |
+
12.088321754138144,4.407535740847106,Medium,Public,83.04164106976468
|
886 |
+
11.363782979252623,4.973594243041073,Medium,Private,81.93801331274273
|
887 |
+
13.693414651472068,5.280161158819607,High,Private,82.04263425017821
|
888 |
+
11.167856370651927,4.190396480078354,Low,Private,85.08642385249489
|
889 |
+
9.28141581842588,5.424061044464089,Low,Public,76.77137419014686
|
890 |
+
11.181309661384619,4.526160652929084,Medium,Public,91.05281099593671
|
891 |
+
12.217407161165816,4.985547729121721,Low,Public,82.71896676336443
|
892 |
+
11.640964362394728,5.546284248003699,Medium,Private,69.9346158875592
|
893 |
+
11.014548062214596,5.006422269810938,Medium,Public,67.64935185526126
|
894 |
+
12.133349379178307,4.5636141094734315,High,Private,84.23186963179411
|
895 |
+
12.338591180891346,4.890390196074169,High,Public,88.51523841754796
|
896 |
+
12.764317982075054,4.911637040794664,High,Public,78.97695087572286
|
897 |
+
11.297419775179286,4.629988970117872,Medium,Private,90.04471686942114
|
898 |
+
9.665763839366292,4.741203937332898,Low,Public,84.75832826970245
|
899 |
+
10.293427372866645,6.598647170504717,High,Private,81.5375593186961
|
900 |
+
12.413017933016715,5.560919447941239,Low,Public,85.86468474972595
|
901 |
+
8.366128658025527,4.704519681970839,High,Public,78.2616879259499
|
902 |
+
10.737346617745802,5.696954404510736,High,Private,89.47009932886027
|
903 |
+
9.213322375345278,4.66618051024399,Medium,Private,78.83659534978445
|
904 |
+
10.057489645869635,6.173124637586641,High,Private,80.31432450947499
|
905 |
+
12.556903725214598,5.369642192198094,High,Public,86.52977672665958
|
906 |
+
10.382198136039806,4.892697855995771,Medium,Public,80.7902444633194
|
907 |
+
10.092873096312298,5.447716925810278,Low,Public,82.97937251771499
|
908 |
+
7.280287718040162,3.4291238479315007,High,Private,61.8541447425799
|
909 |
+
11.492507132054442,3.8732325310901947,Medium,Private,80.37413031048145
|
910 |
+
11.29096836228215,3.8060749936009444,High,Private,80.59325175286439
|
911 |
+
14.32650944661092,5.142943094763707,Medium,Public,86.00319770010654
|
912 |
+
9.384443530093998,6.732514999485092,Low,Public,82.78849178050342
|
913 |
+
10.438300655327877,7.231300146192632,Low,Private,91.32938963278991
|
914 |
+
10.498767367421511,5.638051101440757,Medium,Public,80.14569704252635
|
915 |
+
13.15490655952695,5.500844474944457,Medium,Private,88.01968523500904
|
916 |
+
9.809408935226095,3.1989422658472613,Medium,Public,82.44855507148085
|
917 |
+
10.558043051540679,4.457326200265978,Low,Public,78.60989793318961
|
918 |
+
11.215793019433079,4.212216248091761,High,Private,74.30759439920975
|
919 |
+
10.373218246312716,4.379152401748884,Low,Public,80.39405934068839
|
920 |
+
9.107132770898943,4.83185618141521,Medium,Public,64.45371912993032
|
921 |
+
10.388179985796615,4.527909422302873,Medium,Private,66.07638891523413
|
922 |
+
12.147263499719545,3.020700189624212,Low,Private,80.4906375194567
|
923 |
+
7.9469694011778795,5.7479102561175335,Medium,Public,62.790952271122734
|
924 |
+
10.265939348293752,3.9272569742601933,Low,Private,69.84692544583686
|
925 |
+
8.599758370121654,5.239246864111149,Medium,Public,73.15789421885843
|
926 |
+
12.390093257849685,7.0740826697499735,High,Private,81.7842287715949
|
927 |
+
6.95362619043245,4.080615408413577,High,Public,69.28914297574967
|
928 |
+
8.882156305456823,2.4697124772536703,Medium,Public,77.03323203063664
|
929 |
+
10.754423750129043,4.713995778119889,Low,Private,79.77224522425757
|
930 |
+
13.13104805846868,6.100959634998571,High,Public,81.21693356053791
|
931 |
+
9.868499477854025,6.958347454220593,Low,Public,86.94672550096033
|
932 |
+
8.889600946613603,3.7704504055475834,Medium,Private,66.29059131882762
|
933 |
+
13.76231413888118,5.496699223236522,Low,Private,86.87909465647765
|
934 |
+
7.1039721991675115,4.5346895432199466,High,Public,63.30954883810643
|
935 |
+
5.602388086759836,4.894051645695864,Low,Private,69.80744317071617
|
936 |
+
10.880028900106664,7.644343347017035,Low,Private,81.6334358012759
|
937 |
+
8.995891551294777,3.497029671604847,Medium,Public,78.64851716762867
|
938 |
+
7.9575343657385735,5.253739661494573,Medium,Public,77.39912557969015
|
939 |
+
11.416712894598707,5.467693171765434,Medium,Private,78.09926888688724
|
940 |
+
10.487601427542398,6.085786324939347,High,Private,76.20918413841261
|
941 |
+
8.871842738526544,5.097983153470896,High,Private,73.81517654209888
|
942 |
+
7.439391202658211,5.308050539504102,Medium,Public,82.21871551474428
|
943 |
+
11.74491465656029,4.608351432578209,Medium,Public,88.4976988675283
|
944 |
+
11.300402355917322,5.269126941245108,High,Public,75.66315419685904
|
945 |
+
9.801648272441234,4.656807724502589,High,Public,80.5896348216431
|
946 |
+
13.693273992095332,5.6215718898560505,High,Public,77.1617486744884
|
947 |
+
7.859830467347695,4.630389663956366,High,Public,70.42853312534311
|
948 |
+
6.948949658150522,5.377100393902187,Medium,Public,67.32572814284202
|
949 |
+
8.616183860237511,4.970737186315022,High,Private,80.03487078780977
|
950 |
+
9.908827967289005,6.126050250772169,Medium,Private,72.5531437117942
|
951 |
+
10.486678898645383,4.948606276411514,Low,Public,80.54301838902039
|
952 |
+
9.51752788428734,3.2269675317931186,Low,Public,73.87905767409418
|
953 |
+
10.704110793028594,6.261921500713992,High,Public,79.92385968848062
|
954 |
+
7.496921151619112,4.094267699137381,Low,Private,79.49315860566628
|
955 |
+
12.88752920814652,4.34623366135118,Low,Public,91.09289486999137
|
956 |
+
9.835697643214862,4.404338705956508,Low,Private,75.55593925309964
|
957 |
+
12.234591663176255,6.374438093170817,High,Private,82.30125896310875
|
958 |
+
10.685450692755408,2.8643257860213045,High,Private,68.13302347052694
|
959 |
+
10.913506438307568,8.137748533659995,Low,Private,84.01234074090907
|
960 |
+
11.139534560464408,6.056056809066051,Medium,Private,91.12967061325786
|
961 |
+
10.89541712003463,5.223238914129159,Low,Public,78.98201591362337
|
962 |
+
11.285445519735088,4.945105999383713,High,Public,83.63234645426465
|
963 |
+
12.658305060264862,5.285554074857657,High,Private,76.25745461321989
|
964 |
+
10.39304233940294,5.521122428379338,High,Public,76.89389367270688
|
965 |
+
11.418007515177024,5.645215585950057,Low,Private,83.61616493829239
|
966 |
+
9.820528611425546,5.555604466003448,Medium,Private,78.50377591209651
|
967 |
+
12.880234430898945,5.089580681255248,High,Public,87.51817655573356
|
968 |
+
8.64721539588146,4.802661579692674,High,Public,71.48506373850861
|
969 |
+
13.60188086582163,4.848726716346488,Low,Public,75.72690906204465
|
970 |
+
9.919684098711313,4.805091719647319,Medium,Public,82.65745692850497
|
971 |
+
7.138449795763904,6.133770096867065,Low,Public,68.79680829047386
|
972 |
+
10.256208829821578,5.593556722280862,High,Private,81.43577288057436
|
973 |
+
8.637896685050238,2.0596113653357198,Medium,Public,72.61949475054614
|
974 |
+
11.681287097977448,5.655900776529761,Medium,Public,73.52765938558957
|
975 |
+
8.694752041395219,5.194736194718775,High,Public,80.42723546106873
|
976 |
+
9.10763313357044,4.981291243554103,Medium,Private,72.85436715982719
|
977 |
+
6.220918538108938,4.611481546911114,Medium,Public,69.63433250651936
|
978 |
+
9.095387361501846,6.124112719986013,Low,Private,75.58603098315494
|
979 |
+
5.152241346742087,5.947526462122858,High,Public,77.33872989783302
|
980 |
+
6.832194353028553,4.227122332176391,High,Public,76.722523245242
|
981 |
+
11.520829312288594,5.407052287124535,High,Private,72.09056413641039
|
982 |
+
11.571600317301664,4.028343265458714,High,Public,90.47985114233235
|
983 |
+
10.85091512356993,3.6203818352110693,High,Public,73.74086933738049
|
984 |
+
8.066047713741574,4.3732827754100345,Medium,Private,83.2042029755074
|
985 |
+
9.904577287716041,5.862393472382759,Low,Public,77.32169692330628
|
986 |
+
9.992794921818863,5.953125045376087,High,Private,77.21069388124218
|
987 |
+
7.6832706216149145,5.513085420097293,Low,Private,72.39953070798666
|
988 |
+
13.006796603534301,5.725095786847997,Low,Private,82.59728866244313
|
989 |
+
11.754724581151343,5.5161782871149585,Medium,Private,94.40569928209123
|
990 |
+
9.558071652340672,4.3585184047770476,High,Private,75.27210491524374
|
991 |
+
10.053771677989062,5.431922544673941,Medium,Public,76.74394501029558
|
992 |
+
10.416765615895105,5.800409525227682,High,Public,79.91919930709645
|
993 |
+
5.916530263151419,5.754291329813574,Medium,Public,65.62782153639967
|
994 |
+
9.505645234958806,6.1889133714746425,Low,Private,86.28026319858083
|
995 |
+
8.636031504004404,5.708303847201064,High,Private,68.47911460034474
|
996 |
+
7.996759980210169,5.351448207541583,Low,Private,70.95064923637568
|
997 |
+
9.43779941422809,6.070150238316428,Low,Private,82.66412573516477
|
998 |
+
13.595373053699046,4.97347874075094,Medium,Public,84.86272487236167
|
999 |
+
11.28168572253402,4.118125348772282,High,Public,81.4866353807267
|
1000 |
+
8.857642020434406,4.836933036056236,Low,Private,82.6365041783803
|
1001 |
+
11.145165562712318,4.255097355193551,Medium,Private,83.69198489987735
|
main.py
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# main.py
|
2 |
+
from flask import Flask, jsonify, request
|
3 |
+
from flask_cors import CORS
|
4 |
+
import os
|
5 |
+
import sys
|
6 |
+
|
7 |
+
# Add the 'routers' and 'utils' directories to the Python path
|
8 |
+
# This allows direct imports like 'from routers.preprocess_routes import preprocess_bp'
|
9 |
+
script_dir = os.path.dirname(__file__)
|
10 |
+
sys.path.insert(0, os.path.join(script_dir, 'routers'))
|
11 |
+
sys.path.insert(0, os.path.join(script_dir, 'utils'))
|
12 |
+
|
13 |
+
# Import Blueprints
|
14 |
+
from routers.preprocess_routes import preprocess_bp
|
15 |
+
from routers.discover_routes import discover_bp
|
16 |
+
from routers.intervene_routes import intervene_bp
|
17 |
+
from routers.treatment_routes import treatment_bp
|
18 |
+
from routers.visualize_routes import visualize_bp
|
19 |
+
|
20 |
+
app = Flask(__name__)
|
21 |
+
CORS(app) # Enable CORS for frontend interaction
|
22 |
+
|
23 |
+
# Register Blueprints
|
24 |
+
app.register_blueprint(preprocess_bp, url_prefix='/preprocess')
|
25 |
+
app.register_blueprint(discover_bp, url_prefix='/discover')
|
26 |
+
app.register_blueprint(intervene_bp, url_prefix='/intervene')
|
27 |
+
app.register_blueprint(treatment_bp, url_prefix='/treatment')
|
28 |
+
app.register_blueprint(visualize_bp, url_prefix='/visualize')
|
29 |
+
|
30 |
+
@app.route('/')
|
31 |
+
def home():
|
32 |
+
return "Welcome to CausalBox Backend API!"
|
33 |
+
|
34 |
+
if __name__ == '__main__':
|
35 |
+
# Ensure the 'data' directory exists for storing datasets
|
36 |
+
os.makedirs('data', exist_ok=True)
|
37 |
+
# Run the Flask app
|
38 |
+
app.run(debug=True, host='0.0.0.0', port=5000)
|
nginx.conf
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
events {
|
2 |
+
worker_connections 1024;
|
3 |
+
}
|
4 |
+
|
5 |
+
http {
|
6 |
+
include mime.types;
|
7 |
+
default_type application/octet-stream;
|
8 |
+
|
9 |
+
sendfile on;
|
10 |
+
keepalive_timeout 65;
|
11 |
+
|
12 |
+
# Gzip settings (optional, but good for performance)
|
13 |
+
gzip on;
|
14 |
+
gzip_vary on;
|
15 |
+
gzip_proxied any;
|
16 |
+
gzip_comp_level 6;
|
17 |
+
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
18 |
+
|
19 |
+
server {
|
20 |
+
listen 7860; # Hugging Face Spaces default port for Docker SDK
|
21 |
+
server_name localhost;
|
22 |
+
|
23 |
+
# Serve Streamlit app at the root (/)
|
24 |
+
location / {
|
25 |
+
proxy_pass http://localhost:8501; # Streamlit's default port
|
26 |
+
proxy_http_version 1.1;
|
27 |
+
proxy_set_header Upgrade $http_upgrade;
|
28 |
+
proxy_set_header Connection "upgrade";
|
29 |
+
proxy_set_header Host $host;
|
30 |
+
proxy_cache_bypass $http_upgrade;
|
31 |
+
proxy_read_timeout 300s; # Increase if Streamlit app takes long to load/respond
|
32 |
+
proxy_send_timeout 300s;
|
33 |
+
}
|
34 |
+
|
35 |
+
# Serve Flask API at /api/ (or adjust as per your Flask app's routing)
|
36 |
+
# Note: Your Streamlit app should call http://localhost:5000/preprocess/upload etc. internally.
|
37 |
+
# This Nginx block is for if you wanted to expose Flask directly under /api/,
|
38 |
+
# but for internal communication, Streamlit calls Flask's internal port directly.
|
39 |
+
# However, for the Streamlit app to communicate with Flask, it *must* use `http://localhost:5000`.
|
40 |
+
# The Nginx here is primarily for serving the Streamlit app to the public.
|
41 |
+
# If your Streamlit app's FLASK_API_URL is "http://localhost:5000", Nginx doesn't mediate these internal calls.
|
42 |
+
# The Nginx config below is for direct external access to Flask, which is usually not needed when Streamlit is the frontend.
|
43 |
+
# I'll keep it simple for now, focusing on Streamlit at root.
|
44 |
+
|
45 |
+
# If you wanted to expose Flask on a subpath of the Space URL (e.g., yourspace.hf.space/flask_api)
|
46 |
+
# you would add a location block like this, but your Streamlit app would still call localhost:5000 internally.
|
47 |
+
# location /flask_api/ {
|
48 |
+
# proxy_pass http://localhost:5000/;
|
49 |
+
# proxy_set_header Host $host;
|
50 |
+
# proxy_set_header X-Real-IP $remote_addr;
|
51 |
+
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
52 |
+
# proxy_set_header X-Forwarded-Proto $scheme;
|
53 |
+
# }
|
54 |
+
}
|
55 |
+
}
|
requirements.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1 |
-
|
|
|
2 |
pandas
|
3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Flask
|
2 |
+
flask-cors
|
3 |
pandas
|
4 |
+
numpy
|
5 |
+
scikit-learn
|
6 |
+
causal-learn # For PC algorithm
|
7 |
+
networkx
|
8 |
+
plotly
|
9 |
+
streamlit
|
10 |
+
requests # For Streamlit to communicate with Flask
|
11 |
+
watchfiles # For auto_refresh.py (if implemented for background tasks)
|
routers/__pycache__/discover_routes.cpython-310.pyc
ADDED
Binary file (1.62 kB). View file
|
|
routers/__pycache__/intervene_routes.cpython-310.pyc
ADDED
Binary file (1.62 kB). View file
|
|
routers/__pycache__/preprocess_routes.cpython-310.pyc
ADDED
Binary file (1.93 kB). View file
|
|
routers/__pycache__/treatment_routes.cpython-310.pyc
ADDED
Binary file (2.24 kB). View file
|
|
routers/__pycache__/visualize_routes.cpython-310.pyc
ADDED
Binary file (1.7 kB). View file
|
|
routers/discover_routes.py
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# routers/discover_routes.py
|
2 |
+
from flask import Blueprint, request, jsonify
|
3 |
+
import pandas as pd
|
4 |
+
from utils.casual_algorithms import CausalDiscoveryAlgorithms
|
5 |
+
import logging
|
6 |
+
|
7 |
+
discover_bp = Blueprint('discover', __name__)
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
causal_discovery_algorithms = CausalDiscoveryAlgorithms()
|
11 |
+
|
12 |
+
@discover_bp.route('/', methods=['POST'])
|
13 |
+
def discover_causal_graph():
|
14 |
+
"""
|
15 |
+
Discover causal graph from input data using selected algorithm.
|
16 |
+
Expects 'data' key with list of dicts (preprocessed DataFrame records) and 'algorithm' string.
|
17 |
+
Returns graph as adjacency matrix.
|
18 |
+
"""
|
19 |
+
try:
|
20 |
+
payload = request.json
|
21 |
+
if not payload or 'data' not in payload:
|
22 |
+
return jsonify({"detail": "Invalid request payload: 'data' key missing."}), 400
|
23 |
+
|
24 |
+
df = pd.DataFrame(payload["data"])
|
25 |
+
algorithm = payload.get("algorithm", "pc").lower() # Default to PC
|
26 |
+
|
27 |
+
logger.info(f"Received discovery request with algorithm: {algorithm}, data shape: {df.shape}")
|
28 |
+
|
29 |
+
if algorithm == "pc":
|
30 |
+
adj_matrix = causal_discovery_algorithms.pc_algorithm(df)
|
31 |
+
elif algorithm == "ges":
|
32 |
+
adj_matrix = causal_discovery_algorithms.ges_algorithm(df) # Placeholder
|
33 |
+
elif algorithm == "notears":
|
34 |
+
adj_matrix = causal_discovery_algorithms.notears_algorithm(df) # Placeholder
|
35 |
+
else:
|
36 |
+
return jsonify({"detail": f"Unsupported causal discovery algorithm: {algorithm}"}), 400
|
37 |
+
|
38 |
+
logger.info(f"Causal graph discovered using {algorithm}.")
|
39 |
+
return jsonify({"graph": adj_matrix.tolist()})
|
40 |
+
|
41 |
+
except Exception as e:
|
42 |
+
logger.exception(f"Error in causal discovery: {str(e)}")
|
43 |
+
return jsonify({"detail": f"Causal discovery failed: {str(e)}"}), 500
|
routers/intervene_routes.py
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# routers/intervene_routes.py
|
2 |
+
from flask import Blueprint, request, jsonify
|
3 |
+
import pandas as pd
|
4 |
+
from utils.do_calculus import DoCalculus # Will be used for more advanced intervention
|
5 |
+
import networkx as nx # Assuming graph is passed or re-discovered
|
6 |
+
import logging
|
7 |
+
|
8 |
+
intervene_bp = Blueprint('intervene', __name__)
|
9 |
+
logger = logging.getLogger(__name__)
|
10 |
+
|
11 |
+
@intervene_bp.route('/', methods=['POST'])
|
12 |
+
def perform_intervention():
|
13 |
+
"""
|
14 |
+
Perform causal intervention on data.
|
15 |
+
Expects 'data' (list of dicts), 'intervention_var' (column name),
|
16 |
+
'intervention_value' (numeric), and optionally 'graph' (adjacency matrix).
|
17 |
+
Returns intervened data as list of dicts.
|
18 |
+
"""
|
19 |
+
try:
|
20 |
+
payload = request.json
|
21 |
+
if not payload or 'data' not in payload or 'intervention_var' not in payload or 'intervention_value' not in payload:
|
22 |
+
return jsonify({"detail": "Missing required intervention parameters."}), 400
|
23 |
+
|
24 |
+
df = pd.DataFrame(payload["data"])
|
25 |
+
intervention_var = payload["intervention_var"]
|
26 |
+
intervention_value = payload["intervention_value"]
|
27 |
+
graph_adj_matrix = payload.get("graph") # Optional: pass pre-discovered graph
|
28 |
+
|
29 |
+
logger.info(f"Intervention request: var={intervention_var}, value={intervention_value}, data shape: {df.shape}")
|
30 |
+
|
31 |
+
if intervention_var not in df.columns:
|
32 |
+
return jsonify({"detail": f"Intervention variable '{intervention_var}' not found in data"}), 400
|
33 |
+
|
34 |
+
# For a more advanced do-calculus, you'd need the graph structure.
|
35 |
+
# Here, a simplified direct intervention is applied first.
|
36 |
+
# If graph_adj_matrix is provided, you could convert it to networkx.
|
37 |
+
# For full do-calculus, the DoCalculus class would need a proper graph.
|
38 |
+
|
39 |
+
df_intervened = df.copy()
|
40 |
+
df_intervened[intervention_var] = intervention_value
|
41 |
+
|
42 |
+
# Placeholder for propagating effects using a graph if provided
|
43 |
+
# if graph_adj_matrix:
|
44 |
+
# graph_nx = nx.from_numpy_array(np.array(graph_adj_matrix), create_using=nx.DiGraph)
|
45 |
+
# do_calculus_engine = DoCalculus(graph_nx)
|
46 |
+
# df_intervened = do_calculus_engine.intervene(df_intervened, intervention_var, intervention_value)
|
47 |
+
# logger.info("Propagated effects using do-calculus (simplified).")
|
48 |
+
|
49 |
+
logger.info(f"Intervened data shape: {df_intervened.shape}")
|
50 |
+
return jsonify({"intervened_data": df_intervened.to_dict(orient="records")})
|
51 |
+
|
52 |
+
except Exception as e:
|
53 |
+
logger.exception(f"Error in intervention: {str(e)}")
|
54 |
+
return jsonify({"detail": f"Intervention failed: {str(e)}"}), 500
|
routers/preprocess_routes.py
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# routers/preprocess_routes.py
|
2 |
+
from flask import Blueprint, request, jsonify
|
3 |
+
import pandas as pd
|
4 |
+
from utils.preprocessor import DataPreprocessor
|
5 |
+
import logging
|
6 |
+
|
7 |
+
preprocess_bp = Blueprint('preprocess', __name__)
|
8 |
+
|
9 |
+
# Set up logging
|
10 |
+
logging.basicConfig(level=logging.INFO)
|
11 |
+
logger = logging.getLogger(__name__)
|
12 |
+
|
13 |
+
preprocessor = DataPreprocessor()
|
14 |
+
|
15 |
+
@preprocess_bp.route('/upload', methods=['POST'])
|
16 |
+
def upload_file():
|
17 |
+
"""
|
18 |
+
Upload and preprocess a CSV file.
|
19 |
+
Returns preprocessed DataFrame columns and data as JSON.
|
20 |
+
Optional limit_rows to reduce response size for testing.
|
21 |
+
"""
|
22 |
+
if 'file' not in request.files:
|
23 |
+
return jsonify({"detail": "No file part in the request"}), 400
|
24 |
+
file = request.files['file']
|
25 |
+
if file.filename == '':
|
26 |
+
return jsonify({"detail": "No selected file"}), 400
|
27 |
+
if not file.filename.lower().endswith('.csv'):
|
28 |
+
return jsonify({"detail": "Only CSV files are supported"}), 400
|
29 |
+
|
30 |
+
limit_rows = request.args.get('limit_rows', type=int)
|
31 |
+
|
32 |
+
try:
|
33 |
+
logger.info(f"Received file: {file.filename}")
|
34 |
+
df = pd.read_csv(file)
|
35 |
+
logger.info(f"CSV read successfully, shape: {df.shape}")
|
36 |
+
|
37 |
+
processed_df = preprocessor.preprocess(df)
|
38 |
+
if limit_rows:
|
39 |
+
processed_df = processed_df.head(limit_rows)
|
40 |
+
logger.info(f"Limited to {limit_rows} rows.")
|
41 |
+
|
42 |
+
response = {
|
43 |
+
"columns": list(processed_df.columns),
|
44 |
+
"data": processed_df.to_dict(orient="records")
|
45 |
+
}
|
46 |
+
logger.info(f"Preprocessed {len(response['data'])} records.")
|
47 |
+
return jsonify(response)
|
48 |
+
except pd.errors.EmptyDataError:
|
49 |
+
logger.error("Empty CSV file uploaded.")
|
50 |
+
return jsonify({"detail": "Empty CSV file"}), 400
|
51 |
+
except pd.errors.ParserError:
|
52 |
+
logger.error("Invalid CSV format.")
|
53 |
+
return jsonify({"detail": "Invalid CSV format"}), 400
|
54 |
+
except Exception as e:
|
55 |
+
logger.exception(f"Unexpected error during file processing: {str(e)}")
|
56 |
+
return jsonify({"detail": f"Failed to process file: {str(e)}"}), 500
|
routers/treatment_routes.py
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# routers/treatment_routes.py
|
2 |
+
from flask import Blueprint, request, jsonify
|
3 |
+
import pandas as pd
|
4 |
+
from utils.treatment_effects import TreatmentEffectAlgorithms
|
5 |
+
import logging
|
6 |
+
|
7 |
+
treatment_bp = Blueprint('treatment', __name__)
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
treatment_effect_algorithms = TreatmentEffectAlgorithms()
|
11 |
+
|
12 |
+
@treatment_bp.route('/estimate_ate', methods=['POST'])
|
13 |
+
def estimate_ate():
|
14 |
+
"""
|
15 |
+
Estimate Average Treatment Effect (ATE) or Conditional Treatment Effect (CATE).
|
16 |
+
Expects 'data' (list of dicts), 'treatment_col', 'outcome_col', 'covariates' (list of column names),
|
17 |
+
and 'method' (string for estimation method).
|
18 |
+
Returns ATE/CATE as float or dictionary.
|
19 |
+
"""
|
20 |
+
try:
|
21 |
+
payload = request.json
|
22 |
+
if not payload or 'data' not in payload or 'treatment_col' not in payload or 'outcome_col' not in payload or 'covariates' not in payload:
|
23 |
+
return jsonify({"detail": "Missing required ATE estimation parameters."}), 400
|
24 |
+
|
25 |
+
df = pd.DataFrame(payload["data"])
|
26 |
+
treatment_col = payload["treatment_col"]
|
27 |
+
outcome_col = payload["outcome_col"]
|
28 |
+
covariates = payload["covariates"]
|
29 |
+
method = payload.get("method", "linear_regression").lower() # Default to linear regression
|
30 |
+
|
31 |
+
logger.info(f"ATE/CATE request: treatment={treatment_col}, outcome={outcome_col}, method={method}, data shape: {df.shape}")
|
32 |
+
|
33 |
+
if not all(col in df.columns for col in [treatment_col, outcome_col] + covariates):
|
34 |
+
return jsonify({"detail": "Invalid column names provided for ATE estimation."}), 400
|
35 |
+
|
36 |
+
if method == "linear_regression":
|
37 |
+
result = treatment_effect_algorithms.linear_regression_ate(df, treatment_col, outcome_col, covariates)
|
38 |
+
elif method == "propensity_score_matching":
|
39 |
+
result = treatment_effect_algorithms.propensity_score_matching(df, treatment_col, outcome_col, covariates) # Placeholder
|
40 |
+
elif method == "inverse_propensity_weighting":
|
41 |
+
result = treatment_effect_algorithms.inverse_propensity_weighting(df, treatment_col, outcome_col, covariates) # Placeholder
|
42 |
+
elif method == "t_learner":
|
43 |
+
result = treatment_effect_algorithms.t_learner(df, treatment_col, outcome_col, covariates) # Placeholder
|
44 |
+
elif method == "s_learner":
|
45 |
+
result = treatment_effect_algorithms.s_learner(df, treatment_col, outcome_col, covariates) # Placeholder
|
46 |
+
else:
|
47 |
+
return jsonify({"detail": f"Unsupported treatment effect estimation method: {method}"}), 400
|
48 |
+
|
49 |
+
logger.info(f"Estimated ATE/CATE using {method}: {result}")
|
50 |
+
return jsonify({"result": result})
|
51 |
+
|
52 |
+
except Exception as e:
|
53 |
+
logger.exception(f"Error in ATE/CATE estimation: {str(e)}")
|
54 |
+
return jsonify({"detail": f"ATE/CATE estimation failed: {str(e)}"}), 500
|
routers/visualize_routes.py
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# routers/visualize_routes.py
|
2 |
+
from flask import Blueprint, request, jsonify
|
3 |
+
import pandas as pd
|
4 |
+
from utils.graph_utils import visualize_graph
|
5 |
+
import networkx as nx
|
6 |
+
import numpy as np
|
7 |
+
import logging
|
8 |
+
|
9 |
+
visualize_bp = Blueprint('visualize', __name__)
|
10 |
+
logger = logging.getLogger(__name__)
|
11 |
+
|
12 |
+
@visualize_bp.route('/graph', methods=['POST'])
|
13 |
+
def get_graph_visualization():
|
14 |
+
"""
|
15 |
+
Generate a causal graph visualization from an adjacency matrix.
|
16 |
+
Expects 'graph' (adjacency matrix as list of lists) and 'nodes' (list of node names).
|
17 |
+
Returns Plotly JSON for the graph.
|
18 |
+
"""
|
19 |
+
try:
|
20 |
+
payload = request.json
|
21 |
+
if not payload or 'graph' not in payload or 'nodes' not in payload:
|
22 |
+
return jsonify({"detail": "Missing 'graph' or 'nodes' in request payload."}), 400
|
23 |
+
|
24 |
+
adj_matrix = np.array(payload["graph"])
|
25 |
+
nodes = payload["nodes"]
|
26 |
+
|
27 |
+
logger.info(f"Received graph visualization request for {len(nodes)} nodes.")
|
28 |
+
|
29 |
+
# Reconstruct networkx graph from adjacency matrix and node names
|
30 |
+
graph_nx = nx.from_numpy_array(adj_matrix, create_using=nx.DiGraph)
|
31 |
+
|
32 |
+
# Map integer node labels back to original column names if necessary
|
33 |
+
# Assuming nodes are ordered as they appear in the original dataframe or provided in 'nodes'
|
34 |
+
mapping = {i: node_name for i, node_name in enumerate(nodes)}
|
35 |
+
graph_nx = nx.relabel_nodes(graph_nx, mapping)
|
36 |
+
|
37 |
+
graph_json = visualize_graph(graph_nx)
|
38 |
+
logger.info("Generated graph visualization JSON.")
|
39 |
+
return jsonify({"graph": graph_json})
|
40 |
+
|
41 |
+
except Exception as e:
|
42 |
+
logger.exception(f"Error generating graph visualization: {str(e)}")
|
43 |
+
return jsonify({"detail": f"Failed to generate visualization: {str(e)}"}), 500
|
scripts/generate_data.py
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# scripts/generate_data.py
|
2 |
+
import numpy as np
|
3 |
+
import pandas as pd
|
4 |
+
import os
|
5 |
+
|
6 |
+
def generate_dataset(n_samples=1000):
|
7 |
+
np.random.seed(42)
|
8 |
+
study_hours = np.random.normal(10, 2, n_samples)
|
9 |
+
tuition_hours = np.random.normal(5, 1, n_samples)
|
10 |
+
parental_education = np.random.choice(['High', 'Medium', 'Low'], n_samples)
|
11 |
+
school_type = np.random.choice(['Public', 'Private'], n_samples)
|
12 |
+
exam_score = 50 + 2 * study_hours + 1.5 * tuition_hours + np.random.normal(0, 5, n_samples)
|
13 |
+
|
14 |
+
df = pd.DataFrame({
|
15 |
+
'StudyHours': study_hours,
|
16 |
+
'TuitionHours': tuition_hours,
|
17 |
+
'ParentalEducation': parental_education,
|
18 |
+
'SchoolType': school_type,
|
19 |
+
'FinalExamScore': exam_score
|
20 |
+
})
|
21 |
+
|
22 |
+
# Ensure data directory exists
|
23 |
+
os.makedirs('../data', exist_ok=True)
|
24 |
+
df.to_csv('../data/sample_dataset.csv', index=False)
|
25 |
+
return df
|
26 |
+
|
27 |
+
if __name__ == "__main__":
|
28 |
+
generate_dataset()
|
29 |
+
print("Dataset generated and saved to ../data/sample_dataset.csv")
|
src/streamlit_app.py
DELETED
@@ -1,40 +0,0 @@
|
|
1 |
-
import altair as alt
|
2 |
-
import numpy as np
|
3 |
-
import pandas as pd
|
4 |
-
import streamlit as st
|
5 |
-
|
6 |
-
"""
|
7 |
-
# Welcome to Streamlit!
|
8 |
-
|
9 |
-
Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
|
10 |
-
If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
|
11 |
-
forums](https://discuss.streamlit.io).
|
12 |
-
|
13 |
-
In the meantime, below is an example of what you can do with just a few lines of code:
|
14 |
-
"""
|
15 |
-
|
16 |
-
num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
|
17 |
-
num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
|
18 |
-
|
19 |
-
indices = np.linspace(0, 1, num_points)
|
20 |
-
theta = 2 * np.pi * num_turns * indices
|
21 |
-
radius = indices
|
22 |
-
|
23 |
-
x = radius * np.cos(theta)
|
24 |
-
y = radius * np.sin(theta)
|
25 |
-
|
26 |
-
df = pd.DataFrame({
|
27 |
-
"x": x,
|
28 |
-
"y": y,
|
29 |
-
"idx": indices,
|
30 |
-
"rand": np.random.randn(num_points),
|
31 |
-
})
|
32 |
-
|
33 |
-
st.altair_chart(alt.Chart(df, height=700, width=700)
|
34 |
-
.mark_point(filled=True)
|
35 |
-
.encode(
|
36 |
-
x=alt.X("x", axis=None),
|
37 |
-
y=alt.Y("y", axis=None),
|
38 |
-
color=alt.Color("idx", legend=None, scale=alt.Scale()),
|
39 |
-
size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
|
40 |
-
))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
start.sh
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
|
3 |
+
# Start Flask backend using Gunicorn in the background
|
4 |
+
# Make sure main.py is in flask_backend/ and you reference it correctly.
|
5 |
+
# The `wsgi:app` assumes your Flask app instance is named `app` in `wsgi.py` (common Gunicorn setup)
|
6 |
+
# or you can point directly to `main:app` if `app` is defined in main.py.
|
7 |
+
# Based on your main.py, it's `main:app`.
|
8 |
+
echo "Starting Flask backend..."
|
9 |
+
gunicorn flask_backend.main:app --workers 1 --bind 0.0.0.0:5000 --timeout 600 --log-level debug &
|
10 |
+
|
11 |
+
# Wait a bit for Flask to start (optional, but can help prevent connection errors)
|
12 |
+
sleep 5
|
13 |
+
|
14 |
+
# Start Streamlit frontend in the background
|
15 |
+
# Make sure streamlit_app.py is in streamlit_frontend/
|
16 |
+
echo "Starting Streamlit frontend..."
|
17 |
+
streamlit run streamlit_frontend/streamlit_app.py --server.port 8501 --server.enableCORS false --server.enableXsrfProtection false --server.fileWatcherType none &
|
18 |
+
|
19 |
+
# Wait a bit for Streamlit to start (optional)
|
20 |
+
sleep 5
|
21 |
+
|
22 |
+
# Start Nginx in the foreground (important for Docker containers)
|
23 |
+
echo "Starting Nginx..."
|
24 |
+
nginx -g "daemon off;"
|
streamlit_app.py
ADDED
@@ -0,0 +1,308 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# streamlit_app.py
|
2 |
+
import streamlit as st
|
3 |
+
import pandas as pd
|
4 |
+
import requests
|
5 |
+
import json
|
6 |
+
import plotly.express as px
|
7 |
+
import plotly.graph_objects as go
|
8 |
+
import numpy as np # For random array in placeholders
|
9 |
+
import os
|
10 |
+
|
11 |
+
# Configuration
|
12 |
+
FLASK_API_URL = "http://localhost:5000" # Ensure this matches your Flask app's host and port
|
13 |
+
|
14 |
+
st.set_page_config(layout="wide", page_title="CausalBox Toolkit")
|
15 |
+
|
16 |
+
st.title("🔬 CausalBox: A Causal Inference Toolkit")
|
17 |
+
st.markdown("Uncover causal relationships, simulate interventions, and estimate treatment effects.")
|
18 |
+
|
19 |
+
# --- Session State Initialization ---
|
20 |
+
if 'processed_data' not in st.session_state:
|
21 |
+
st.session_state.processed_data = None
|
22 |
+
if 'processed_columns' not in st.session_state:
|
23 |
+
st.session_state.processed_columns = None
|
24 |
+
if 'causal_graph_adj' not in st.session_state:
|
25 |
+
st.session_state.causal_graph_adj = None
|
26 |
+
if 'causal_graph_nodes' not in st.session_state:
|
27 |
+
st.session_state.causal_graph_nodes = None
|
28 |
+
|
29 |
+
# --- Data Preprocessing Module ---
|
30 |
+
st.header("1. Data Preprocessor 🧹")
|
31 |
+
st.write("Upload your CSV dataset or use a generated sample dataset.")
|
32 |
+
|
33 |
+
# Option to use generated sample dataset
|
34 |
+
if st.button("Use Sample Dataset (sample_dataset.csv)"):
|
35 |
+
# In a real scenario, Streamlit would serve the file or you'd load it directly if local.
|
36 |
+
# For this setup, we assume the Flask backend can access it or you manually upload it once.
|
37 |
+
# For demonstration, we'll simulate loading a generic DataFrame.
|
38 |
+
# In a full deployment, you'd have a mechanism to either:
|
39 |
+
# a) Have Flask serve the sample file, or
|
40 |
+
# b) Directly load it in Streamlit if the app and data are co-located.
|
41 |
+
try:
|
42 |
+
# Assuming the sample dataset is accessible or you are testing locally with `scripts/generate_data.py`
|
43 |
+
# and then manually uploading this generated file.
|
44 |
+
# For simplicity, we'll create a dummy df here if not actually uploaded.
|
45 |
+
sample_df_path = "data/sample_dataset.csv" # Path relative to main.py or Streamlit app execution
|
46 |
+
if os.path.exists(sample_df_path):
|
47 |
+
sample_df = pd.read_csv(sample_df_path)
|
48 |
+
st.success(f"Loaded sample dataset from {sample_df_path}. Please upload this file if running from different directory.")
|
49 |
+
else:
|
50 |
+
st.warning("Sample dataset not found at data/sample_dataset.csv.")
|
51 |
+
# Dummy DataFrame for demonstration if sample file isn't found
|
52 |
+
sample_df = pd.DataFrame(np.random.rand(10, 5), columns=[f'col_{i}' for i in range(5)])
|
53 |
+
|
54 |
+
# Convert to JSON for Flask API call
|
55 |
+
files = {'file': ('sample_dataset.csv', sample_df.to_csv(index=False), 'text/csv')}
|
56 |
+
response = requests.post(f"{FLASK_API_URL}/preprocess/upload", files=files)
|
57 |
+
|
58 |
+
if response.status_code == 200:
|
59 |
+
result = response.json()
|
60 |
+
st.session_state.processed_data = result['data']
|
61 |
+
st.session_state.processed_columns = result['columns']
|
62 |
+
st.success("Sample dataset preprocessed successfully!")
|
63 |
+
st.dataframe(pd.DataFrame(st.session_state.processed_data).head()) # Display first few rows
|
64 |
+
else:
|
65 |
+
st.error(f"Error preprocessing sample dataset: {response.json().get('detail', 'Unknown error')}")
|
66 |
+
except Exception as e:
|
67 |
+
st.error(f"Could not load or process sample dataset: {e}")
|
68 |
+
|
69 |
+
|
70 |
+
uploaded_file = st.file_uploader("Choose a CSV file", type="csv")
|
71 |
+
if uploaded_file is not None:
|
72 |
+
st.info("Uploading and preprocessing data...")
|
73 |
+
files = {'file': (uploaded_file.name, uploaded_file.getvalue(), 'text/csv')}
|
74 |
+
try:
|
75 |
+
response = requests.post(f"{FLASK_API_URL}/preprocess/upload", files=files)
|
76 |
+
if response.status_code == 200:
|
77 |
+
result = response.json()
|
78 |
+
st.session_state.processed_data = result['data']
|
79 |
+
st.session_state.processed_columns = result['columns']
|
80 |
+
st.success("File preprocessed successfully!")
|
81 |
+
st.dataframe(pd.DataFrame(st.session_state.processed_data).head()) # Display first few rows
|
82 |
+
else:
|
83 |
+
st.error(f"Error during preprocessing: {response.json().get('detail', 'Unknown error')}")
|
84 |
+
except requests.exceptions.ConnectionError:
|
85 |
+
st.error(f"Could not connect to Flask API at {FLASK_API_URL}. Please ensure the backend is running.")
|
86 |
+
except Exception as e:
|
87 |
+
st.error(f"An unexpected error occurred: {e}")
|
88 |
+
|
89 |
+
# --- Causal Discovery Module ---
|
90 |
+
st.header("2. Causal Discovery 🕵️♂️")
|
91 |
+
if st.session_state.processed_data:
|
92 |
+
st.write("Learn the causal structure from your preprocessed data.")
|
93 |
+
|
94 |
+
discovery_algo = st.selectbox(
|
95 |
+
"Select Causal Discovery Algorithm:",
|
96 |
+
("PC Algorithm", "GES (Greedy Equivalence Search) - Placeholder", "NOTEARS - Placeholder")
|
97 |
+
)
|
98 |
+
|
99 |
+
if st.button("Discover Causal Graph"):
|
100 |
+
st.info(f"Discovering graph using {discovery_algo}...")
|
101 |
+
algo_map = {
|
102 |
+
"PC Algorithm": "pc",
|
103 |
+
"GES (Greedy Equivalence Search) - Placeholder": "ges",
|
104 |
+
"NOTEARS - Placeholder": "notears"
|
105 |
+
}
|
106 |
+
selected_algo_code = algo_map[discovery_algo]
|
107 |
+
|
108 |
+
try:
|
109 |
+
response = requests.post(
|
110 |
+
f"{FLASK_API_URL}/discover/",
|
111 |
+
json={"data": st.session_state.processed_data, "algorithm": selected_algo_code}
|
112 |
+
)
|
113 |
+
if response.status_code == 200:
|
114 |
+
result = response.json()
|
115 |
+
st.session_state.causal_graph_adj = result['graph']
|
116 |
+
st.session_state.causal_graph_nodes = st.session_state.processed_columns
|
117 |
+
st.success("Causal graph discovered!")
|
118 |
+
st.subheader("Causal Graph Visualization")
|
119 |
+
# Visualization will be handled by the Causal Graph Visualizer section
|
120 |
+
else:
|
121 |
+
st.error(f"Error during causal discovery: {response.json().get('detail', 'Unknown error')}")
|
122 |
+
except requests.exceptions.ConnectionError:
|
123 |
+
st.error(f"Could not connect to Flask API at {FLASK_API_URL}. Please ensure the backend is running.")
|
124 |
+
except Exception as e:
|
125 |
+
st.error(f"An unexpected error occurred: {e}")
|
126 |
+
else:
|
127 |
+
st.info("Please preprocess data first to enable causal discovery.")
|
128 |
+
|
129 |
+
# --- Causal Graph Visualizer Module ---
|
130 |
+
st.header("3. Causal Graph Visualizer 📊")
|
131 |
+
if st.session_state.causal_graph_adj and st.session_state.causal_graph_nodes:
|
132 |
+
st.write("Interactive visualization of the discovered causal graph.")
|
133 |
+
try:
|
134 |
+
response = requests.post(
|
135 |
+
f"{FLASK_API_URL}/visualize/graph",
|
136 |
+
json={"graph": st.session_state.causal_graph_adj, "nodes": st.session_state.causal_graph_nodes}
|
137 |
+
)
|
138 |
+
if response.status_code == 200:
|
139 |
+
graph_json = response.json()['graph']
|
140 |
+
fig = go.Figure(json.loads(graph_json))
|
141 |
+
st.plotly_chart(fig, use_container_width=True)
|
142 |
+
st.markdown("""
|
143 |
+
**Graph Explanation:**
|
144 |
+
* **Nodes:** Represent variables in your dataset.
|
145 |
+
* **Arrows (Edges):** Indicate a direct causal influence from one variable (the tail) to another (the head).
|
146 |
+
* **No Arrow:** Suggests no direct causal relationship was found, or the relationship is mediated by other variables.
|
147 |
+
|
148 |
+
This graph helps answer "Why did it happen?" by showing the structural relationships.
|
149 |
+
""")
|
150 |
+
else:
|
151 |
+
st.error(f"Error visualizing graph: {response.json().get('detail', 'Unknown error')}")
|
152 |
+
except requests.exceptions.ConnectionError:
|
153 |
+
st.error(f"Could not connect to Flask API at {FLASK_API_URL}. Please ensure the backend is running.")
|
154 |
+
except Exception as e:
|
155 |
+
st.error(f"An unexpected error occurred during visualization: {e}")
|
156 |
+
else:
|
157 |
+
st.info("Please discover a causal graph first to visualize it.")
|
158 |
+
|
159 |
+
|
160 |
+
# --- Do-Calculus Engine Module ---
|
161 |
+
st.header("4. Do-Calculus Engine 🧪")
|
162 |
+
if st.session_state.processed_data and st.session_state.causal_graph_adj:
|
163 |
+
st.write("Simulate interventions and observe their effects based on the causal graph.")
|
164 |
+
|
165 |
+
intervention_var = st.selectbox(
|
166 |
+
"Select variable to intervene on:",
|
167 |
+
st.session_state.processed_columns,
|
168 |
+
key="inter_var_select"
|
169 |
+
)
|
170 |
+
# Attempt to infer type for intervention_value input
|
171 |
+
# Simplified approach: assuming numerical for now due to preprocessor output
|
172 |
+
if intervention_var and isinstance(st.session_state.processed_data[0][intervention_var], (int, float)):
|
173 |
+
intervention_value = st.number_input(f"Set '{intervention_var}' to value:", key="inter_val_input")
|
174 |
+
else: # Treat as string/categorical for input, then try to preprocess for API
|
175 |
+
intervention_value = st.text_input(f"Set '{intervention_var}' to value:", key="inter_val_input_text")
|
176 |
+
st.warning("Categorical intervention values might require specific encoding logic on the backend.")
|
177 |
+
|
178 |
+
if st.button("Perform Intervention"):
|
179 |
+
st.info(f"Performing intervention: do('{intervention_var}' = {intervention_value})...")
|
180 |
+
try:
|
181 |
+
response = requests.post(
|
182 |
+
f"{FLASK_API_URL}/intervene/",
|
183 |
+
json={
|
184 |
+
"data": st.session_state.processed_data,
|
185 |
+
"intervention_var": intervention_var,
|
186 |
+
"intervention_value": intervention_value,
|
187 |
+
"graph": st.session_state.causal_graph_adj # Pass graph for advanced do-calculus
|
188 |
+
}
|
189 |
+
)
|
190 |
+
if response.status_code == 200:
|
191 |
+
intervened_data = pd.DataFrame(response.json()['intervened_data'])
|
192 |
+
st.success("Intervention simulated successfully!")
|
193 |
+
st.subheader("Intervened Data (First 10 rows)")
|
194 |
+
st.dataframe(intervened_data.head(10))
|
195 |
+
|
196 |
+
# Simple comparison visualization (e.g., histogram of outcome variable)
|
197 |
+
if st.session_state.processed_columns and 'FinalExamScore' in st.session_state.processed_columns:
|
198 |
+
original_df = pd.DataFrame(st.session_state.processed_data)
|
199 |
+
fig_dist = go.Figure()
|
200 |
+
fig_dist.add_trace(go.Histogram(x=original_df['FinalExamScore'], name='Original', opacity=0.7))
|
201 |
+
fig_dist.add_trace(go.Histogram(x=intervened_data['FinalExamScore'], name='Intervened', opacity=0.0))
|
202 |
+
|
203 |
+
st.plotly_chart(fig_dist, use_container_width=True)
|
204 |
+
st.markdown("""
|
205 |
+
**Intervention Explanation:**
|
206 |
+
* By simulating `do(X=x)`, we are forcing the value of X, effectively breaking its causal links from its parents.
|
207 |
+
* The graph above shows the distribution of a key outcome variable (e.g., `FinalExamScore`) before and after the intervention.
|
208 |
+
* This helps answer "What if we do this instead?" by showing the predicted outcome.
|
209 |
+
""")
|
210 |
+
else:
|
211 |
+
st.info("Consider adding a relevant outcome variable to your dataset for better intervention analysis.")
|
212 |
+
else:
|
213 |
+
st.error(f"Error during intervention: {response.json().get('detail', 'Unknown error')}")
|
214 |
+
except requests.exceptions.ConnectionError:
|
215 |
+
st.error(f"Could not connect to Flask API at {FLASK_API_URL}. Please ensure the backend is running.")
|
216 |
+
except Exception as e:
|
217 |
+
st.error(f"An unexpected error occurred during intervention: {e}")
|
218 |
+
else:
|
219 |
+
st.info("Please preprocess data and discover a causal graph first to perform interventions.")
|
220 |
+
|
221 |
+
# --- Treatment Effect Estimator Module ---
|
222 |
+
st.header("5. Treatment Effect Estimator 🎯")
|
223 |
+
if st.session_state.processed_data:
|
224 |
+
st.write("Estimate Average Treatment Effect (ATE) or Conditional Treatment Effect (CATE).")
|
225 |
+
|
226 |
+
col1, col2 = st.columns(2)
|
227 |
+
with col1:
|
228 |
+
treatment_col = st.selectbox(
|
229 |
+
"Select Treatment Variable:",
|
230 |
+
st.session_state.processed_columns,
|
231 |
+
key="treat_col_select"
|
232 |
+
)
|
233 |
+
with col2:
|
234 |
+
outcome_col = st.selectbox(
|
235 |
+
"Select Outcome Variable:",
|
236 |
+
st.session_state.processed_columns,
|
237 |
+
key="outcome_col_select"
|
238 |
+
)
|
239 |
+
|
240 |
+
all_cols_except_treat_outcome = [col for col in st.session_state.processed_columns if col not in [treatment_col, outcome_col]]
|
241 |
+
covariates = st.multiselect(
|
242 |
+
"Select Covariates (confounders):",
|
243 |
+
all_cols_except_treat_outcome,
|
244 |
+
default=all_cols_except_treat_outcome, # Default to all other columns
|
245 |
+
key="covariates_select"
|
246 |
+
)
|
247 |
+
|
248 |
+
estimation_method = st.selectbox(
|
249 |
+
"Select Estimation Method:",
|
250 |
+
(
|
251 |
+
"Linear Regression ATE",
|
252 |
+
"Propensity Score Matching - Placeholder",
|
253 |
+
"Inverse Propensity Weighting - Placeholder",
|
254 |
+
"T-learner - Placeholder",
|
255 |
+
"S-learner - Placeholder"
|
256 |
+
)
|
257 |
+
)
|
258 |
+
|
259 |
+
if st.button("Estimate Treatment Effect"):
|
260 |
+
st.info(f"Estimating treatment effect using {estimation_method}...")
|
261 |
+
method_map = {
|
262 |
+
"Linear Regression ATE": "linear_regression",
|
263 |
+
"Propensity Score Matching - Placeholder": "propensity_score_matching",
|
264 |
+
"Inverse Propensity Weighting - Placeholder": "inverse_propensity_weighting",
|
265 |
+
"T-learner - Placeholder": "t_learner",
|
266 |
+
"S-learner - Placeholder": "s_learner"
|
267 |
+
}
|
268 |
+
selected_method_code = method_map[estimation_method]
|
269 |
+
|
270 |
+
try:
|
271 |
+
response = requests.post(
|
272 |
+
f"{FLASK_API_URL}/treatment/estimate_ate",
|
273 |
+
json={
|
274 |
+
"data": st.session_state.processed_data,
|
275 |
+
"treatment_col": treatment_col,
|
276 |
+
"outcome_col": outcome_col,
|
277 |
+
"covariates": covariates,
|
278 |
+
"method": selected_method_code
|
279 |
+
}
|
280 |
+
)
|
281 |
+
if response.status_code == 200:
|
282 |
+
ate_result = response.json()['result']
|
283 |
+
st.success(f"Treatment effect estimated using {estimation_method}:")
|
284 |
+
st.write(f"**Estimated ATE: {ate_result:.4f}**")
|
285 |
+
st.markdown("""
|
286 |
+
**Treatment Effect Explanation:**
|
287 |
+
* **Average Treatment Effect (ATE):** Measures the average causal effect of a treatment (e.g., `StudyHours`) on an outcome (e.g., `FinalExamScore`) across the entire population.
|
288 |
+
* It answers "How much does doing X cause a change in Y?".
|
289 |
+
* This estimation attempts to control for confounders (variables that influence both treatment and outcome) to isolate the true causal effect.
|
290 |
+
""")
|
291 |
+
else:
|
292 |
+
st.error(f"Error during ATE estimation: {response.json().get('detail', 'Unknown error')}")
|
293 |
+
except requests.exceptions.ConnectionError:
|
294 |
+
st.error(f"Could not connect to Flask API at {FLASK_API_URL}. Please ensure the backend is running.")
|
295 |
+
except Exception as e:
|
296 |
+
st.error(f"An unexpected error occurred during ATE estimation: {e}")
|
297 |
+
else:
|
298 |
+
st.info("Please preprocess data first to estimate treatment effects.")
|
299 |
+
|
300 |
+
# --- Optional Advanced Add-Ons (Future Considerations) ---
|
301 |
+
st.header("Optional Advanced Add-Ons (Future Work) 🚀")
|
302 |
+
st.markdown("""
|
303 |
+
- **🔄 Auto-causal graph refresh if dataset updates:** This would involve setting up a background process (e.g., using `watchfiles` with a separate service or integrated carefully into Flask/Streamlit) that monitors changes to the source CSV file. Upon detection, it would re-run the preprocessing and causal discovery, updating the dashboard live. This requires more complex architecture (e.g., WebSockets for real-time updates to Streamlit or scheduled background tasks).
|
304 |
+
- **🕰️ Time-Series Causal Discovery (e.g., Granger Causality):** This requires handling time-indexed data and implementing algorithms specifically designed for temporal causal relationships. It would involve a separate data input and discovery module.
|
305 |
+
""")
|
306 |
+
|
307 |
+
st.markdown("---")
|
308 |
+
st.info("Developed by CausalBox Team. For support, please contact us.")
|
utils/__pycache__/casual_algorithms.cpython-310.pyc
ADDED
Binary file (2.11 kB). View file
|
|
utils/__pycache__/do_calculus.cpython-310.pyc
ADDED
Binary file (1.49 kB). View file
|
|
utils/__pycache__/graph_utils.cpython-310.pyc
ADDED
Binary file (1.61 kB). View file
|
|
utils/__pycache__/preprocessor.cpython-310.pyc
ADDED
Binary file (2.23 kB). View file
|
|
utils/__pycache__/treatment_effects.cpython-310.pyc
ADDED
Binary file (2.6 kB). View file
|
|
utils/auto_refresh.py
ADDED
File without changes
|
utils/casual_algorithms.py
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# utils/causal_algorithms.py
|
2 |
+
import networkx as nx
|
3 |
+
import pandas as pd
|
4 |
+
import numpy as np
|
5 |
+
from causallearn.search.ConstraintBased.PC import pc
|
6 |
+
# from causallearn.search.ScoreBased.GES import ges # Example import for GES
|
7 |
+
# from notears import notears_linear # Example import for NOTEARS
|
8 |
+
|
9 |
+
class CausalDiscoveryAlgorithms:
|
10 |
+
def pc_algorithm(self, df, alpha=0.05):
|
11 |
+
"""
|
12 |
+
Run PC algorithm to learn causal graph.
|
13 |
+
Returns a directed graph's adjacency matrix.
|
14 |
+
Requires numerical data.
|
15 |
+
"""
|
16 |
+
data_array = df.to_numpy()
|
17 |
+
cg = pc(data_array, alpha=alpha, indep_test="fisherz")
|
18 |
+
adj_matrix = cg.G.graph
|
19 |
+
return adj_matrix
|
20 |
+
|
21 |
+
def ges_algorithm(self, df):
|
22 |
+
"""
|
23 |
+
Placeholder for GES (Greedy Equivalence Search) algorithm.
|
24 |
+
Returns a directed graph's adjacency matrix.
|
25 |
+
You would implement or integrate the GES algorithm here.
|
26 |
+
"""
|
27 |
+
# Example: G, edges = ges(data_array)
|
28 |
+
# For now, returning a simplified correlation-based graph for demonstration
|
29 |
+
print("GES algorithm is a placeholder. Using a simplified correlation-based graph.")
|
30 |
+
G = nx.DiGraph()
|
31 |
+
nodes = df.columns
|
32 |
+
G.add_nodes_from(nodes)
|
33 |
+
corr_matrix = df.corr().abs()
|
34 |
+
threshold = 0.3
|
35 |
+
for i, col1 in enumerate(nodes):
|
36 |
+
for col2 in nodes[i+1:]:
|
37 |
+
if corr_matrix.loc[col1, col2] > threshold:
|
38 |
+
if np.random.rand() > 0.5:
|
39 |
+
G.add_edge(col1, col2)
|
40 |
+
else:
|
41 |
+
G.add_edge(col2, col1)
|
42 |
+
return nx.to_numpy_array(G) # Convert to adjacency matrix
|
43 |
+
|
44 |
+
def notears_algorithm(self, df):
|
45 |
+
"""
|
46 |
+
Placeholder for NOTEARS algorithm.
|
47 |
+
Returns a directed graph's adjacency matrix.
|
48 |
+
You would implement or integrate the NOTEARS algorithm here.
|
49 |
+
"""
|
50 |
+
# Example: W_est = notears_linear(data_array)
|
51 |
+
print("NOTEARS algorithm is a placeholder. Using a simplified correlation-based graph.")
|
52 |
+
G = nx.DiGraph()
|
53 |
+
nodes = df.columns
|
54 |
+
G.add_nodes_from(nodes)
|
55 |
+
corr_matrix = df.corr().abs()
|
56 |
+
threshold = 0.3
|
57 |
+
for i, col1 in enumerate(nodes):
|
58 |
+
for col2 in nodes[i+1:]:
|
59 |
+
if corr_matrix.loc[col1, col2] > threshold:
|
60 |
+
if np.random.rand() > 0.5:
|
61 |
+
G.add_edge(col1, col2)
|
62 |
+
else:
|
63 |
+
G.add_edge(col2, col1)
|
64 |
+
return nx.to_numpy_array(G) # Convert to adjacency matrix
|
utils/do_calculus.py
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# utils/do_calculus.py
|
2 |
+
import pandas as pd
|
3 |
+
import numpy as np
|
4 |
+
import networkx as nx
|
5 |
+
|
6 |
+
class DoCalculus:
|
7 |
+
def __init__(self, graph):
|
8 |
+
self.graph = graph
|
9 |
+
|
10 |
+
def intervene(self, data, intervention_var, intervention_value):
|
11 |
+
"""
|
12 |
+
Simulate do(X=x) intervention on a variable.
|
13 |
+
Returns intervened DataFrame.
|
14 |
+
This is a simplified implementation.
|
15 |
+
"""
|
16 |
+
intervened_data = data.copy()
|
17 |
+
|
18 |
+
# Direct intervention: set the value
|
19 |
+
intervened_data[intervention_var] = intervention_value
|
20 |
+
|
21 |
+
# Propagate effects (simplified linear model) - needs graph
|
22 |
+
# For a true do-calculus, you'd prune the graph and re-estimate based on parents
|
23 |
+
# For demonstration, this still uses a simplified propagation.
|
24 |
+
try:
|
25 |
+
# Ensure graph is connected and topological sort is possible
|
26 |
+
if self.graph and not nx.is_directed_acyclic_graph(self.graph):
|
27 |
+
print("Warning: Graph is not a DAG. Topological sort may fail or be incorrect for do-calculus.")
|
28 |
+
|
29 |
+
# This simplified propagation is a conceptual placeholder
|
30 |
+
for node in nx.topological_sort(self.graph):
|
31 |
+
if node == intervention_var:
|
32 |
+
continue # Do not propagate back to the intervened variable
|
33 |
+
|
34 |
+
parents = list(self.graph.predecessors(node))
|
35 |
+
if parents:
|
36 |
+
# Very simplified linear model to show propagation
|
37 |
+
# In reality, you'd use learned coefficients or structural equations
|
38 |
+
combined_effect = np.zeros(len(intervened_data))
|
39 |
+
for p in parents:
|
40 |
+
if p in intervened_data.columns:
|
41 |
+
# Use a fixed random coefficient for demonstration
|
42 |
+
coeff = 0.5
|
43 |
+
combined_effect += intervened_data[p].to_numpy() * coeff
|
44 |
+
|
45 |
+
# Add a small random noise to simulate uncertainty
|
46 |
+
intervened_data[node] += combined_effect + np.random.normal(0, 0.1, len(intervened_data))
|
47 |
+
except Exception as e:
|
48 |
+
print(f"Could not perform full propagation due to graph issues or simplification: {e}")
|
49 |
+
# Fallback to direct intervention only if graph logic fails
|
50 |
+
pass # The direct intervention `intervened_data[intervention_var] = intervention_value` is already applied
|
51 |
+
|
52 |
+
return intervened_data
|
utils/graph_utils.py
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# utils/graph_utils.py
|
2 |
+
import networkx as nx
|
3 |
+
import plotly.graph_objects as go
|
4 |
+
|
5 |
+
def visualize_graph(graph):
|
6 |
+
"""
|
7 |
+
Visualize a causal graph using Plotly.
|
8 |
+
Returns Plotly figure as JSON.
|
9 |
+
"""
|
10 |
+
# Use a fixed seed for layout reproducibility (optional)
|
11 |
+
pos = nx.spring_layout(graph, seed=42)
|
12 |
+
|
13 |
+
edge_x, edge_y = [], []
|
14 |
+
for edge in graph.edges():
|
15 |
+
x0, y0 = pos[edge[0]]
|
16 |
+
x1, y1 = pos[edge[1]]
|
17 |
+
edge_x.extend([x0, x1, None])
|
18 |
+
edge_y.extend([y0, y1, None])
|
19 |
+
|
20 |
+
edge_trace = go.Scatter(
|
21 |
+
x=edge_x, y=edge_y,
|
22 |
+
line=dict(width=1, color='#888'),
|
23 |
+
mode='lines',
|
24 |
+
hoverinfo='none'
|
25 |
+
)
|
26 |
+
|
27 |
+
node_x, node_y = [], []
|
28 |
+
for node in graph.nodes():
|
29 |
+
x, y = pos[node]
|
30 |
+
node_x.append(x)
|
31 |
+
node_y.append(y)
|
32 |
+
|
33 |
+
node_trace = go.Scatter(
|
34 |
+
x=node_x, y=node_y,
|
35 |
+
mode='markers+text',
|
36 |
+
text=list(graph.nodes()),
|
37 |
+
textposition='bottom center',
|
38 |
+
marker=dict(size=15, color='lightblue', line=dict(width=2, color='DarkSlateGrey')),
|
39 |
+
hoverinfo='text'
|
40 |
+
)
|
41 |
+
|
42 |
+
fig = go.Figure(
|
43 |
+
data=[edge_trace, node_trace],
|
44 |
+
layout=go.Layout(
|
45 |
+
showlegend=False,
|
46 |
+
hovermode='closest',
|
47 |
+
margin=dict(b=20, l=5, r=5, t=40),
|
48 |
+
annotations=[dict(
|
49 |
+
text="Python Causal Graph",
|
50 |
+
showarrow=False,
|
51 |
+
xref="paper", yref="paper",
|
52 |
+
x=0.005, y= -0.002,
|
53 |
+
font=dict(size=14, color="lightgray")
|
54 |
+
)],
|
55 |
+
xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
|
56 |
+
yaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
|
57 |
+
title=dict(text="Causal Graph Visualization", font=dict(size=16)) # Corrected line
|
58 |
+
)
|
59 |
+
)
|
60 |
+
return fig.to_json()
|
utils/preprocessor.py
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# utils/preprocessor.py
|
2 |
+
from sklearn.preprocessing import StandardScaler, LabelEncoder
|
3 |
+
import pandas as pd
|
4 |
+
import numpy as np
|
5 |
+
import logging
|
6 |
+
|
7 |
+
# Set up logging
|
8 |
+
logging.basicConfig(level=logging.INFO)
|
9 |
+
logger = logging.getLogger(__name__)
|
10 |
+
|
11 |
+
class DataPreprocessor:
|
12 |
+
def __init__(self):
|
13 |
+
self.scaler = StandardScaler()
|
14 |
+
self.label_encoders = {}
|
15 |
+
|
16 |
+
def preprocess(self, df):
|
17 |
+
"""
|
18 |
+
Preprocess DataFrame: handle missing values, encode categorical variables, scale numerical variables.
|
19 |
+
"""
|
20 |
+
try:
|
21 |
+
logger.info(f"Input DataFrame shape: {df.shape}, columns: {list(df.columns)}")
|
22 |
+
df_processed = df.copy()
|
23 |
+
|
24 |
+
# Handle missing values
|
25 |
+
logger.info("Handling missing values...")
|
26 |
+
for col in df_processed.columns:
|
27 |
+
if df_processed[col].isnull().any():
|
28 |
+
if pd.api.types.is_numeric_dtype(df_processed[col]):
|
29 |
+
df_processed[col] = df_processed[col].fillna(df_processed[col].mean())
|
30 |
+
logger.info(f"Filled numeric missing values in '{col}' with mean.")
|
31 |
+
else:
|
32 |
+
df_processed[col] = df_processed[col].fillna(df_processed[col].mode()[0])
|
33 |
+
logger.info(f"Filled categorical missing values in '{col}' with mode.")
|
34 |
+
|
35 |
+
# Encode categorical variables
|
36 |
+
logger.info("Encoding categorical variables...")
|
37 |
+
for col in df_processed.select_dtypes(include=['object', 'category']).columns:
|
38 |
+
logger.info(f"Encoding column: {col}")
|
39 |
+
self.label_encoders[col] = LabelEncoder()
|
40 |
+
df_processed[col] = self.label_encoders[col].fit_transform(df_processed[col])
|
41 |
+
|
42 |
+
# Scale numerical variables
|
43 |
+
logger.info("Scaling numerical variables...")
|
44 |
+
numeric_cols = df_processed.select_dtypes(include=[np.number]).columns
|
45 |
+
if len(numeric_cols) > 0:
|
46 |
+
# Exclude columns that are now effectively categorical (post-label encoding)
|
47 |
+
# This is a heuristic; ideally, identify original numeric columns.
|
48 |
+
cols_to_scale = [col for col in numeric_cols if col not in self.label_encoders]
|
49 |
+
if cols_to_scale:
|
50 |
+
df_processed[cols_to_scale] = self.scaler.fit_transform(df_processed[cols_to_scale])
|
51 |
+
logger.info(f"Scaled numeric columns: {cols_to_scale}")
|
52 |
+
|
53 |
+
logger.info(f"Preprocessed DataFrame shape: {df_processed.shape}")
|
54 |
+
return df_processed
|
55 |
+
except Exception as e:
|
56 |
+
logger.exception(f"Error preprocessing data: {str(e)}")
|
57 |
+
raise
|
utils/treatment_effects.py
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# utils/treatment_effects.py
|
2 |
+
from sklearn.linear_model import LinearRegression, LogisticRegression
|
3 |
+
import pandas as pd
|
4 |
+
import numpy as np
|
5 |
+
# For matching-based methods, you might need libraries like dowhy or causalml
|
6 |
+
# import statsmodels.api as sm # Example for regression diagnostics
|
7 |
+
|
8 |
+
class TreatmentEffectAlgorithms:
|
9 |
+
def linear_regression_ate(self, df, treatment_col, outcome_col, covariates):
|
10 |
+
"""
|
11 |
+
Estimate ATE using linear regression.
|
12 |
+
"""
|
13 |
+
X = df[covariates + [treatment_col]]
|
14 |
+
y = df[outcome_col]
|
15 |
+
model = LinearRegression()
|
16 |
+
model.fit(X, y)
|
17 |
+
ate = model.coef_[-1] # Coefficient of treatment_col
|
18 |
+
return float(ate)
|
19 |
+
|
20 |
+
def propensity_score_matching(self, df, treatment_col, outcome_col, covariates):
|
21 |
+
"""
|
22 |
+
Placeholder for Propensity Score Matching.
|
23 |
+
You would implement or integrate a matching algorithm here.
|
24 |
+
"""
|
25 |
+
print("Propensity Score Matching is a placeholder. Returning a dummy ATE.")
|
26 |
+
# Simplified: Estimate propensity scores
|
27 |
+
X_propensity = df[covariates]
|
28 |
+
T_propensity = df[treatment_col]
|
29 |
+
prop_model = LogisticRegression(solver='liblinear')
|
30 |
+
prop_model.fit(X_propensity, T_propensity)
|
31 |
+
propensity_scores = prop_model.predict_proba(X_propensity)[:, 1]
|
32 |
+
|
33 |
+
# Dummy ATE calculation for demonstration
|
34 |
+
treated_outcome = df[df[treatment_col] == 1][outcome_col].mean()
|
35 |
+
control_outcome = df[df[treatment_col] == 0][outcome_col].mean()
|
36 |
+
return float(treated_outcome - control_outcome) # Simplified dummy ATE
|
37 |
+
|
38 |
+
def inverse_propensity_weighting(self, df, treatment_col, outcome_col, covariates):
|
39 |
+
"""
|
40 |
+
Placeholder for Inverse Propensity Weighting (IPW).
|
41 |
+
You would implement or integrate IPW here.
|
42 |
+
"""
|
43 |
+
print("Inverse Propensity Weighting is a placeholder. Returning a dummy ATE.")
|
44 |
+
# Dummy ATE for demonstration
|
45 |
+
return np.random.rand() * 10 # Random dummy value
|
46 |
+
|
47 |
+
def t_learner(self, df, treatment_col, outcome_col, covariates):
|
48 |
+
"""
|
49 |
+
Placeholder for T-learner.
|
50 |
+
You would implement a T-learner using two separate models.
|
51 |
+
"""
|
52 |
+
print("T-learner is a placeholder. Returning a dummy ATE.")
|
53 |
+
# Dummy ATE for demonstration
|
54 |
+
return np.random.rand() * 10 + 5 # Random dummy value
|
55 |
+
|
56 |
+
def s_learner(self, df, treatment_col, outcome_col, covariates):
|
57 |
+
"""
|
58 |
+
Placeholder for S-learner.
|
59 |
+
You would implement an S-learner using a single model.
|
60 |
+
"""
|
61 |
+
print("S-learner is a placeholder. Returning a dummy ATE.")
|
62 |
+
# Dummy ATE for demonstration
|
63 |
+
return np.random.rand() * 10 - 2 # Random dummy value
|