Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- data/figures/developer_engagement_journey_2024-03-04.png +0 -0
- data/figures/developer_survival_curve_2024-03-04.png +0 -0
- data/source/all_networks_developer_classification.csv +0 -0
- debug.csv +0 -0
- github_metrics/__pycache__/utils.cpython-311.pyc +0 -0
- github_metrics/developer_survival_plot.py +174 -0
- github_metrics/main.py +343 -85
- github_metrics/utils.py +24 -0
- poetry.lock +283 -1
- pyproject.toml +6 -0
data/figures/developer_engagement_journey_2024-03-04.png
ADDED
|
data/figures/developer_survival_curve_2024-03-04.png
ADDED
|
data/source/all_networks_developer_classification.csv
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
debug.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
github_metrics/__pycache__/utils.cpython-311.pyc
ADDED
|
Binary file (1.7 kB). View file
|
|
|
github_metrics/developer_survival_plot.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import matplotlib.pyplot as plt
|
| 2 |
+
import numpy as np
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import seaborn as sns
|
| 5 |
+
from lifelines import KaplanMeierFitter
|
| 6 |
+
from matplotlib.colors import LinearSegmentedColormap
|
| 7 |
+
|
| 8 |
+
from utils import save_plot
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def load_and_prepare_data(file_path):
|
| 12 |
+
"""
|
| 13 |
+
Load CSV data, convert 'month_year' to datetime, and prepare cohort and duration calculations.
|
| 14 |
+
Filter data to include only entries from 2021 onwards and adjust the cohort calculation based on the first active month.
|
| 15 |
+
Additionally, eliminate all months with a negative 'Order' so we only get the months after the cohort of the individual.
|
| 16 |
+
"""
|
| 17 |
+
df = pd.read_csv(file_path)
|
| 18 |
+
df["month_year"] = pd.to_datetime(df["month_year"], format="%B_%Y")
|
| 19 |
+
df = df[df["month_year"] >= "2021-09-01"]
|
| 20 |
+
df["Active"] = df["total_commits"] > 0
|
| 21 |
+
df.sort_values(by=["developer", "month_year"], inplace=True)
|
| 22 |
+
|
| 23 |
+
first_active_month = (
|
| 24 |
+
df[df["Active"]].groupby("developer")["month_year"].min().reset_index()
|
| 25 |
+
)
|
| 26 |
+
first_active_month.rename(columns={"month_year": "FirstActiveMonth"}, inplace=True)
|
| 27 |
+
|
| 28 |
+
df = df.merge(first_active_month, on="developer", how="left")
|
| 29 |
+
|
| 30 |
+
df["Cohort"] = df["FirstActiveMonth"].dt.to_period("M")
|
| 31 |
+
|
| 32 |
+
def calculate_order(row):
|
| 33 |
+
if pd.isnull(row["Cohort"]):
|
| 34 |
+
return None
|
| 35 |
+
return (row["month_year"].to_period("M") - row["Cohort"]).n
|
| 36 |
+
|
| 37 |
+
df["Order"] = df.apply(calculate_order, axis=1)
|
| 38 |
+
|
| 39 |
+
df = df[df["Order"] >= 0]
|
| 40 |
+
df["Inactive_Month"] = df.groupby("developer")["Active"].transform(
|
| 41 |
+
lambda x: x.rolling(window=2, min_periods=2).sum() == 0
|
| 42 |
+
)
|
| 43 |
+
df["inactive_for_two_months"] = (
|
| 44 |
+
df.groupby("developer")["Inactive_Month"].transform("max").astype(int)
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
df["duration"] = df.groupby("developer")["month_year"].transform("nunique")
|
| 48 |
+
df.to_csv("debug.csv", index=False)
|
| 49 |
+
|
| 50 |
+
return df
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def visualize_developer_retention(df):
|
| 54 |
+
cohort_counts = (
|
| 55 |
+
df[~df["Inactive_Month"]]
|
| 56 |
+
.groupby(["Cohort", "Order"])
|
| 57 |
+
.developer.nunique()
|
| 58 |
+
.unstack(0)
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
cohort_sizes = cohort_counts.iloc[0]
|
| 62 |
+
retention = cohort_counts.divide(cohort_sizes, axis=1)
|
| 63 |
+
|
| 64 |
+
colors = [(0, "#FF0000"), (0.15, "#FFA500"), (0.2, "#FFFF00"), (1, "#008000")]
|
| 65 |
+
cmap = LinearSegmentedColormap.from_list("custom_cmap", colors, N=256)
|
| 66 |
+
plt.figure(figsize=(12, 8)) # Adjusted figure size for better visibility
|
| 67 |
+
sns.heatmap(retention.T, annot=False, cmap=cmap)
|
| 68 |
+
plt.title("Journey Through Code: Tracking Developer Engagement Over Time", pad=20)
|
| 69 |
+
|
| 70 |
+
plt.subplots_adjust(bottom=0.3)
|
| 71 |
+
|
| 72 |
+
description_text = (
|
| 73 |
+
"This heatmap visualizes the engagement journey of developers, tracked monthly across cohorts."
|
| 74 |
+
" Each cohort represents developers who began contributing in the same month."
|
| 75 |
+
" The color gradient from red to green signifies the evolution of active engagement over time,"
|
| 76 |
+
" with red indicating lower engagement levels and green denoting higher activity."
|
| 77 |
+
" Cohorts are plotted on the y-axis, and the actual months since the start of the cohort on the x-axis."
|
| 78 |
+
" This visualization offers insights into how developer activity trends evolve,"
|
| 79 |
+
" highlighting periods of increased or decreased engagement and aiding in understanding"
|
| 80 |
+
" the effectiveness of retention strategies over time."
|
| 81 |
+
" Parameters:"
|
| 82 |
+
"(a) A developer is considered inactive if they have at least 2 continuous inactive months."
|
| 83 |
+
"(b) With one commit in a month, the developer is considered active."
|
| 84 |
+
"(c) The data is filtered to include only entries from September 2021 onwards."
|
| 85 |
+
)
|
| 86 |
+
plt.figtext(0.5, -0.0001, description_text, ha="center", fontsize=9, wrap=True)
|
| 87 |
+
|
| 88 |
+
save_plot(plt, "developer_engagement_journey")
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def survival_curve_analysis_and_plot(df):
|
| 92 |
+
"""
|
| 93 |
+
Perform analysis on the DataFrame to calculate durations and generate visualizations, with annotations explaining the analysis.
|
| 94 |
+
Adjust the event definition and perform Log-Rank Test.
|
| 95 |
+
"""
|
| 96 |
+
summary_df = (
|
| 97 |
+
df.groupby("developer")
|
| 98 |
+
.agg({"duration": "first", "inactive_for_two_months": "last"})
|
| 99 |
+
.reset_index()
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
kmf = KaplanMeierFitter()
|
| 103 |
+
kmf.fit(
|
| 104 |
+
durations=summary_df["duration"],
|
| 105 |
+
event_observed=summary_df["inactive_for_two_months"],
|
| 106 |
+
label="Developer Survival Probability",
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
plt.figure(figsize=(10, 6))
|
| 110 |
+
ax = plt.subplot(111)
|
| 111 |
+
kmf.plot_survival_function(ax=ax)
|
| 112 |
+
|
| 113 |
+
plt.title("Developer Survival Curve: Probability of Active Contribution Over Time")
|
| 114 |
+
plt.grid(True, which="both", linestyle="--", linewidth=0.5)
|
| 115 |
+
median_survival_time = kmf.median_survival_time_
|
| 116 |
+
ax.axhline(y=0.5, color="red", linestyle="--")
|
| 117 |
+
ax.text(
|
| 118 |
+
median_survival_time,
|
| 119 |
+
0.48,
|
| 120 |
+
"Median Survival Time",
|
| 121 |
+
verticalalignment="center",
|
| 122 |
+
color="red",
|
| 123 |
+
fontsize=8,
|
| 124 |
+
)
|
| 125 |
+
ax.axvline(x=3, color="green", linestyle="--")
|
| 126 |
+
ax.text(
|
| 127 |
+
3,
|
| 128 |
+
0.95,
|
| 129 |
+
"Inactive Month + 1",
|
| 130 |
+
verticalalignment="top",
|
| 131 |
+
horizontalalignment="center",
|
| 132 |
+
color="green",
|
| 133 |
+
fontsize=8,
|
| 134 |
+
)
|
| 135 |
+
ax.axvline(x=median_survival_time, color="green", linestyle="--")
|
| 136 |
+
ax.text(
|
| 137 |
+
len(df["duration"].unique()),
|
| 138 |
+
0.9,
|
| 139 |
+
f"After month {int(median_survival_time)} the probability of developers staying is lower than 50 percent",
|
| 140 |
+
verticalalignment="top",
|
| 141 |
+
horizontalalignment="right",
|
| 142 |
+
color="green",
|
| 143 |
+
fontsize=8,
|
| 144 |
+
)
|
| 145 |
+
ax.set_yticks(np.arange(0, 1.1, 0.1))
|
| 146 |
+
|
| 147 |
+
# Setting the x-axis and y-axis labels as per the request
|
| 148 |
+
plt.xlabel("Months since the developer started committing code")
|
| 149 |
+
plt.ylabel("Probability of a developer staying in the ecosystem")
|
| 150 |
+
|
| 151 |
+
description_text = (
|
| 152 |
+
"The Kaplan-Meier survival curve shows the probability of developers continuing to contribute over time."
|
| 153 |
+
"Parameters:"
|
| 154 |
+
"(a) A developer is consider as inactive if they have at least 2 continuous inactive months."
|
| 155 |
+
"(b) With one commit in a month, the developer is considered active."
|
| 156 |
+
"(c) The data is filtered to include only entries from September 2021 onwards."
|
| 157 |
+
"The Kaplan-Meier estimator is a non-parametric statistic used to estimate the survival function from lifetime data."
|
| 158 |
+
"It requires to know the duration each subject was observed for, and whether the event of interest"
|
| 159 |
+
"(in this case, becoming inactive for two months) was observed."
|
| 160 |
+
"The 'Median Survival Time' shows when the chance of further contributions drops below 50%. "
|
| 161 |
+
"This analysis helps in understanding the retention of developers and predicting future contribution patterns."
|
| 162 |
+
)
|
| 163 |
+
plt.figtext(0.1, -0.1, description_text, ha="left", fontsize=8, wrap=True)
|
| 164 |
+
|
| 165 |
+
save_plot(plt, "developer_survival_curve")
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
if __name__ == "__main__":
|
| 169 |
+
csv_path = "data/source/all_networks_developer_classification.csv"
|
| 170 |
+
df = load_and_prepare_data(csv_path)
|
| 171 |
+
|
| 172 |
+
visualize_developer_retention(df)
|
| 173 |
+
|
| 174 |
+
survival_curve_analysis_and_plot(df)
|
github_metrics/main.py
CHANGED
|
@@ -1,112 +1,370 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import pandas as pd
|
| 3 |
-
import
|
| 4 |
-
|
| 5 |
from termcolor import colored
|
|
|
|
|
|
|
| 6 |
|
| 7 |
-
|
| 8 |
-
def load_dataset():
|
| 9 |
-
try:
|
| 10 |
-
print(colored("Loading dataset...", "blue"))
|
| 11 |
-
df = pd.read_csv("data/source/all_networks_developer_classification.csv")
|
| 12 |
-
# Ensure the month_year column is in the correct datetime format
|
| 13 |
-
df['month_year'] = pd.to_datetime(df['month_year'], format='%B_%Y') # Adjust format if necessary
|
| 14 |
-
return df
|
| 15 |
-
except Exception as e:
|
| 16 |
-
print(colored(f"Error loading dataset: {e}", "red"))
|
| 17 |
-
raise
|
| 18 |
-
|
| 19 |
-
# Process input and generate plot and classification with debug prints
|
| 20 |
-
def process_input(input_text, uploaded_file):
|
| 21 |
try:
|
| 22 |
print(colored("Processing input...", "blue"))
|
| 23 |
-
|
| 24 |
-
# Check if a file was uploaded
|
| 25 |
if uploaded_file is not None:
|
| 26 |
print(colored("Reading from uploaded file...", "blue"))
|
| 27 |
-
# Decode the bytes object to string
|
| 28 |
file_content = uploaded_file.decode("utf-8")
|
| 29 |
-
|
| 30 |
-
github_handles = [handle.strip() for handle in file_content.split('\n') if handle.strip()]
|
| 31 |
else:
|
| 32 |
github_handles = [handle.strip() for handle in input_text.split(",")]
|
| 33 |
-
|
| 34 |
print(colored(f"GitHub handles: {github_handles}", "blue"))
|
| 35 |
|
| 36 |
-
|
| 37 |
-
df = load_dataset()
|
| 38 |
-
|
| 39 |
-
# Filter dataset for the provided GitHub handles
|
| 40 |
print(colored("Filtering dataset...", "blue"))
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
|
|
|
|
|
|
| 58 |
print(colored("Classifying developers...", "blue"))
|
| 59 |
-
|
| 60 |
-
for handle in github_handles:
|
| 61 |
-
dev_df = filtered_df[filtered_df['developer'] == handle]
|
| 62 |
-
last_3_months = pd.Timestamp.now() - pd.DateOffset(months=3)
|
| 63 |
-
recent_activity = dev_df[dev_df['month_year'] >= last_3_months]
|
| 64 |
-
total_recent_commits = recent_activity['total_commits'].sum()
|
| 65 |
-
|
| 66 |
-
if dev_df.empty:
|
| 67 |
-
status = "Always been inactive"
|
| 68 |
-
elif recent_activity.empty:
|
| 69 |
-
status = "Previously active but no longer"
|
| 70 |
-
elif total_recent_commits < 20:
|
| 71 |
-
status = "Low-level active"
|
| 72 |
-
else:
|
| 73 |
-
status = "Highly involved"
|
| 74 |
-
|
| 75 |
-
classification.append((handle, status))
|
| 76 |
-
|
| 77 |
-
classification_df = pd.DataFrame(classification, columns=["Developer", "Classification"]).sort_values("Classification", ascending=False)
|
| 78 |
print(colored("Classification completed.", "blue"))
|
| 79 |
|
| 80 |
-
|
| 81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
except Exception as e:
|
| 83 |
print(colored(f"Error processing input: {e}", "red"))
|
| 84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
-
# Gradio interface with descriptions and debug prints
|
| 87 |
with gr.Blocks() as app:
|
| 88 |
-
gr.Markdown("
|
| 89 |
-
gr.Markdown(
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
with gr.Row():
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
|
| 109 |
if __name__ == "__main__":
|
| 110 |
print(colored("Launching app...", "blue"))
|
| 111 |
-
app.launch(share=True)
|
| 112 |
-
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import pandas as pd
|
| 3 |
+
import plotly.express as px
|
| 4 |
+
import plotly.graph_objects as go
|
| 5 |
from termcolor import colored
|
| 6 |
+
from scipy.stats import mannwhitneyu
|
| 7 |
+
from utils import load_all_developers_dataset
|
| 8 |
|
| 9 |
+
def process_input(input_text, uploaded_file, program_end_date=None, event_name=None):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
try:
|
| 11 |
print(colored("Processing input...", "blue"))
|
|
|
|
|
|
|
| 12 |
if uploaded_file is not None:
|
| 13 |
print(colored("Reading from uploaded file...", "blue"))
|
|
|
|
| 14 |
file_content = uploaded_file.decode("utf-8")
|
| 15 |
+
github_handles = [handle.strip() for handle in file_content.split("\n") if handle.strip()]
|
|
|
|
| 16 |
else:
|
| 17 |
github_handles = [handle.strip() for handle in input_text.split(",")]
|
|
|
|
| 18 |
print(colored(f"GitHub handles: {github_handles}", "blue"))
|
| 19 |
|
| 20 |
+
df = load_all_developers_dataset()
|
|
|
|
|
|
|
|
|
|
| 21 |
print(colored("Filtering dataset...", "blue"))
|
| 22 |
+
one_year_ago = pd.Timestamp.now() - pd.DateOffset(years=1)
|
| 23 |
+
filtered_df = df[(df["developer"].isin(github_handles)) & (df["month_year"] >= one_year_ago)]
|
| 24 |
+
filtered_df = filtered_df.sort_values(by=["developer", "month_year"])
|
| 25 |
+
filtered_df.loc[:, "month_year"] = pd.to_datetime(filtered_df["month_year"])
|
| 26 |
+
|
| 27 |
+
line_fig = create_line_plot(filtered_df, github_handles, program_end_date)
|
| 28 |
+
analysis_result = perform_statistical_analysis(filtered_df, github_handles, program_end_date)
|
| 29 |
+
new_developers_count = count_new_developers(filtered_df, github_handles, program_end_date)
|
| 30 |
+
|
| 31 |
+
last_3_months = pd.Timestamp.now() - pd.DateOffset(months=3)
|
| 32 |
+
recent_activity_user = filtered_df[filtered_df["month_year"] >= last_3_months]
|
| 33 |
+
all_devs_df = load_all_developers_dataset()
|
| 34 |
+
all_devs_filtered_df = all_devs_df[(all_devs_df["month_year"] >= last_3_months)]
|
| 35 |
+
other_devs_recent_activity = all_devs_filtered_df[~all_devs_filtered_df["developer"].isin(github_handles)]
|
| 36 |
+
|
| 37 |
+
user_specified_active = recent_activity_user[recent_activity_user["total_commits"] > 0]
|
| 38 |
+
other_developers_active = other_devs_recent_activity[other_devs_recent_activity["total_commits"] > 0]
|
| 39 |
+
box_fig = create_box_plot(user_specified_active, other_developers_active)
|
| 40 |
+
|
| 41 |
print(colored("Classifying developers...", "blue"))
|
| 42 |
+
classification_df = classify_developers(github_handles, recent_activity_user)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
print(colored("Classification completed.", "blue"))
|
| 44 |
|
| 45 |
+
comparison_result = compare_user_developers_to_others(user_specified_active, other_developers_active, df, program_end_date)
|
| 46 |
+
growth_rate_result = compare_growth_rate(user_specified_active, other_developers_active, df)
|
| 47 |
+
|
| 48 |
+
tldr_summary = generate_tldr_summary(github_handles, classification_df, analysis_result, new_developers_count, comparison_result, growth_rate_result, event_name)
|
| 49 |
+
|
| 50 |
+
return line_fig, box_fig, classification_df, analysis_result, new_developers_count, comparison_result, growth_rate_result, tldr_summary
|
| 51 |
except Exception as e:
|
| 52 |
print(colored(f"Error processing input: {e}", "red"))
|
| 53 |
+
return None, None, None, None, "Error in processing input.", None, None, "Error in processing input."
|
| 54 |
+
|
| 55 |
+
def create_line_plot(filtered_df, github_handles, program_end_date):
|
| 56 |
+
all_developers = pd.DataFrame({"developer": github_handles, "month_year": pd.Timestamp.now(), "total_commits": 0})
|
| 57 |
+
plot_df = pd.concat([filtered_df, all_developers])
|
| 58 |
+
plot_df = plot_df.groupby(["developer", "month_year"])["total_commits"].sum().reset_index()
|
| 59 |
+
line_fig = px.line(
|
| 60 |
+
plot_df,
|
| 61 |
+
x="month_year",
|
| 62 |
+
y="total_commits",
|
| 63 |
+
color="developer",
|
| 64 |
+
labels={"month_year": "Month", "total_commits": "Number of Commits"},
|
| 65 |
+
title="Commits per Month",
|
| 66 |
+
)
|
| 67 |
+
if program_end_date:
|
| 68 |
+
program_end_date = pd.to_datetime(program_end_date)
|
| 69 |
+
line_fig.add_vline(x=program_end_date, line_width=2, line_dash="dash", line_color="red")
|
| 70 |
+
return line_fig
|
| 71 |
+
|
| 72 |
+
def create_box_plot(user_specified_active, other_developers_active):
|
| 73 |
+
box_fig = go.Figure()
|
| 74 |
+
box_fig.add_trace(go.Box(y=user_specified_active["total_commits"], name="User Specified Developers"))
|
| 75 |
+
box_fig.add_trace(go.Box(y=other_developers_active["total_commits"], name="Other Developers"))
|
| 76 |
+
box_fig.update_layout(
|
| 77 |
+
title="Comparison of Monthly Commits in the Last 3 Months: User Specified vs. Other Developers (Active Only)",
|
| 78 |
+
yaxis_title="Total Monthly Commits",
|
| 79 |
+
yaxis=dict(range=[0, 50]),
|
| 80 |
+
)
|
| 81 |
+
return box_fig
|
| 82 |
+
|
| 83 |
+
def classify_developers(github_handles, recent_activity_user):
|
| 84 |
+
classification = []
|
| 85 |
+
for handle in github_handles:
|
| 86 |
+
dev_df = recent_activity_user[recent_activity_user["developer"] == handle]
|
| 87 |
+
total_recent_commits = dev_df["total_commits"].sum()
|
| 88 |
+
if dev_df.empty or total_recent_commits == 0:
|
| 89 |
+
status = "Always been inactive"
|
| 90 |
+
elif total_recent_commits < 20:
|
| 91 |
+
status = "Low-level active"
|
| 92 |
+
else:
|
| 93 |
+
status = "Highly involved"
|
| 94 |
+
classification.append((handle, status, total_recent_commits))
|
| 95 |
+
|
| 96 |
+
sort_keys = {
|
| 97 |
+
"Highly involved": 1,
|
| 98 |
+
"Low-level active": 2,
|
| 99 |
+
"Previously active but no longer": 3,
|
| 100 |
+
"Always been inactive": 4,
|
| 101 |
+
}
|
| 102 |
+
classification_df = pd.DataFrame(classification, columns=["Developer", "Classification", "Total Recent Commits"])
|
| 103 |
+
classification_df["Sort Key"] = classification_df["Classification"].map(sort_keys)
|
| 104 |
+
classification_df.sort_values(by=["Sort Key", "Total Recent Commits"], ascending=[True, False], inplace=True)
|
| 105 |
+
classification_df.drop(["Sort Key", "Total Recent Commits"], axis=1, inplace=True)
|
| 106 |
+
return classification_df
|
| 107 |
+
|
| 108 |
+
def perform_statistical_analysis(filtered_df, github_handles, program_end_date_str):
|
| 109 |
+
if program_end_date_str is None:
|
| 110 |
+
return "Program end date not provided. Unable to perform statistical analysis."
|
| 111 |
+
|
| 112 |
+
program_end_date = pd.to_datetime(program_end_date_str)
|
| 113 |
+
before_program = filtered_df[filtered_df["month_year"] < program_end_date]
|
| 114 |
+
after_program = filtered_df[filtered_df["month_year"] >= program_end_date]
|
| 115 |
+
|
| 116 |
+
before_counts = before_program.groupby("developer")["total_commits"].median()
|
| 117 |
+
after_counts = after_program.groupby("developer")["total_commits"].median()
|
| 118 |
+
|
| 119 |
+
all_developers = pd.Series(0, index=github_handles)
|
| 120 |
+
before_counts = before_counts.reindex(all_developers.index, fill_value=0)
|
| 121 |
+
after_counts = after_counts.reindex(all_developers.index, fill_value=0)
|
| 122 |
+
|
| 123 |
+
if len(before_counts) < 2 or len(after_counts) < 2:
|
| 124 |
+
return "Not enough data for statistical analysis."
|
| 125 |
+
|
| 126 |
+
stat, p_value = mannwhitneyu(after_counts, before_counts)
|
| 127 |
+
analysis_result = f"Mann-Whitney U test statistic: {stat:.3f}, P-value: {p_value:.3f}\n"
|
| 128 |
+
|
| 129 |
+
if p_value < 0.2:
|
| 130 |
+
if stat > 0:
|
| 131 |
+
analysis_result += "Difference in commit activity before and after the program is considered significant. " \
|
| 132 |
+
"The commit activity is higher after the program."
|
| 133 |
+
else:
|
| 134 |
+
analysis_result += "Difference in commit activity before and after the program is considered significant. " \
|
| 135 |
+
"The commit activity is lower after the program."
|
| 136 |
+
else:
|
| 137 |
+
analysis_result += "No significant difference in commit activity before and after the program."
|
| 138 |
+
|
| 139 |
+
return analysis_result
|
| 140 |
+
|
| 141 |
+
def count_new_developers(filtered_df, github_handles, program_end_date_str):
|
| 142 |
+
if program_end_date_str is None:
|
| 143 |
+
return "Program end date not provided. Unable to count new developers."
|
| 144 |
+
|
| 145 |
+
program_end_date = pd.to_datetime(program_end_date_str)
|
| 146 |
+
two_months_after_program = program_end_date + pd.DateOffset(months=2)
|
| 147 |
+
|
| 148 |
+
before_program = filtered_df[filtered_df["month_year"] < program_end_date]
|
| 149 |
+
after_program = filtered_df[(filtered_df["month_year"] >= program_end_date) & (filtered_df["month_year"] <= two_months_after_program)]
|
| 150 |
+
|
| 151 |
+
before_developers = before_program["developer"].unique()
|
| 152 |
+
after_developers = after_program["developer"].unique()
|
| 153 |
+
|
| 154 |
+
new_developers = set(after_developers) - set(before_developers)
|
| 155 |
+
new_developers_str = ", ".join(new_developers)
|
| 156 |
+
|
| 157 |
+
return f"Number of new developers committing code within 2 months after the program: {len(new_developers)}\nNew developers: {new_developers_str}"
|
| 158 |
+
|
| 159 |
+
def compare_user_developers_to_others(user_specified_active, other_developers_active, df, program_end_date_str):
|
| 160 |
+
if program_end_date_str is None:
|
| 161 |
+
return "Program end date not provided. Unable to compare user-specified developers to others."
|
| 162 |
+
|
| 163 |
+
program_end_date = pd.to_datetime(program_end_date_str)
|
| 164 |
+
|
| 165 |
+
user_commits = df[(df["developer"].isin(user_specified_active["developer"])) & (df["month_year"] >= program_end_date)]["total_commits"]
|
| 166 |
+
other_commits = df[(df["developer"].isin(other_developers_active["developer"])) & (df["month_year"] >= program_end_date)]["total_commits"]
|
| 167 |
+
|
| 168 |
+
stat, p_value = mannwhitneyu(user_commits, other_commits)
|
| 169 |
+
comparison_result = f"Mann-Whitney U test statistic: {stat:.3f}, P-value: {p_value:.3f}\n"
|
| 170 |
+
|
| 171 |
+
if p_value < 0.25:
|
| 172 |
+
if stat > 0:
|
| 173 |
+
comparison_result += "The user-specified developers have a significantly higher number of commits compared to other developers since the program end date."
|
| 174 |
+
else:
|
| 175 |
+
comparison_result += "The user-specified developers have a significantly lower number of commits compared to other developers since the program end date."
|
| 176 |
+
else:
|
| 177 |
+
comparison_result += "There is no significant difference in the number of commits between user-specified developers and other developers since the program end date."
|
| 178 |
+
|
| 179 |
+
return comparison_result
|
| 180 |
+
|
| 181 |
+
def compare_growth_rate(user_specified_active, other_developers_active, df):
|
| 182 |
+
user_growth_rates = []
|
| 183 |
+
other_growth_rates = []
|
| 184 |
+
|
| 185 |
+
for developer in user_specified_active["developer"].unique():
|
| 186 |
+
user_df = df[df["developer"] == developer]
|
| 187 |
+
user_df = user_df.sort_values("month_year")
|
| 188 |
+
user_commits = user_df["total_commits"].tolist()
|
| 189 |
+
user_growth_rate = calculate_average_growth_rate(user_commits)
|
| 190 |
+
user_growth_rates.append(user_growth_rate)
|
| 191 |
+
|
| 192 |
+
for developer in other_developers_active["developer"].unique():
|
| 193 |
+
other_df = df[df["developer"] == developer]
|
| 194 |
+
other_df = other_df.sort_values("month_year")
|
| 195 |
+
other_commits = other_df["total_commits"].tolist()
|
| 196 |
+
other_growth_rate = calculate_average_growth_rate(other_commits)
|
| 197 |
+
other_growth_rates.append(other_growth_rate)
|
| 198 |
+
|
| 199 |
+
stat, p_value = mannwhitneyu(user_growth_rates, other_growth_rates)
|
| 200 |
+
comparison_result = f"Mann-Whitney U test statistic: {stat:.3f}, P-value: {p_value:.3f}\n"
|
| 201 |
+
|
| 202 |
+
if p_value < 0.25:
|
| 203 |
+
if stat > 0:
|
| 204 |
+
comparison_result += "The user-specified developers have a significantly higher average growth rate of commit activity compared to other developers."
|
| 205 |
+
else:
|
| 206 |
+
comparison_result += "The user-specified developers have a significantly lower average growth rate of commit activity compared to other developers."
|
| 207 |
+
else:
|
| 208 |
+
comparison_result += "There is no significant difference in the average growth rate of commit activity between user-specified developers and other developers."
|
| 209 |
+
|
| 210 |
+
return comparison_result
|
| 211 |
+
|
| 212 |
+
def calculate_average_growth_rate(commits):
|
| 213 |
+
growth_rates = []
|
| 214 |
+
for i in range(1, len(commits)):
|
| 215 |
+
if commits[i - 1] != 0:
|
| 216 |
+
growth_rate = (commits[i] - commits[i - 1]) / commits[i - 1]
|
| 217 |
+
growth_rates.append(growth_rate)
|
| 218 |
+
if len(growth_rates) > 0:
|
| 219 |
+
return sum(growth_rates) / len(growth_rates)
|
| 220 |
+
else:
|
| 221 |
+
return 0
|
| 222 |
+
|
| 223 |
+
def generate_tldr_summary(github_handles, classification_df, analysis_result, new_developers_count, comparison_result, growth_rate_result, event_name):
|
| 224 |
+
summary = f"### 📝 TLDR Summary for {', '.join(github_handles)}\n\n"
|
| 225 |
+
|
| 226 |
+
highly_involved_devs = classification_df[classification_df["Classification"] == "Highly involved"]["Developer"].tolist()
|
| 227 |
+
if highly_involved_devs:
|
| 228 |
+
summary += f"**🌟 High Performers:** {', '.join(highly_involved_devs)}\n\n"
|
| 229 |
+
|
| 230 |
+
if "higher after the program" in analysis_result:
|
| 231 |
+
summary += "**📈 Commit Activity:** Significantly higher after the program.\n\n"
|
| 232 |
+
elif "lower after the program" in analysis_result:
|
| 233 |
+
summary += "**📉 Commit Activity:** Significantly lower after the program.\n\n"
|
| 234 |
+
else:
|
| 235 |
+
summary += "**🔄 Commit Activity:** No significant change after the program.\n\n"
|
| 236 |
+
|
| 237 |
+
if new_developers_count.startswith("Number of new developers"):
|
| 238 |
+
summary += f"**🆕 New Developers:** {new_developers_count.split(':')[1].strip()}\n\n"
|
| 239 |
+
|
| 240 |
+
if "significantly higher number of commits" in comparison_result:
|
| 241 |
+
summary += "**🔍 Comparison with Other Developers:** User-specified developers have a significantly higher number of commits.\n\n"
|
| 242 |
+
elif "significantly lower number of commits" in comparison_result:
|
| 243 |
+
summary += "**🔍 Comparison with Other Developers:** User-specified developers have a significantly lower number of commits.\n\n"
|
| 244 |
+
else:
|
| 245 |
+
summary += "**🔍 Comparison with Other Developers:** No significant difference in the number of commits.\n\n"
|
| 246 |
+
|
| 247 |
+
if "significantly higher average growth rate" in growth_rate_result:
|
| 248 |
+
summary += "**📈 Growth Rate:** User-specified developers have a significantly higher average growth rate.\n\n"
|
| 249 |
+
elif "significantly lower average growth rate" in growth_rate_result:
|
| 250 |
+
summary += "**📉 Growth Rate:** User-specified developers have a significantly lower average growth rate.\n\n"
|
| 251 |
+
else:
|
| 252 |
+
summary += "**🔄 Growth Rate:** No significant difference in the average growth rate.\n\n"
|
| 253 |
+
|
| 254 |
+
if event_name:
|
| 255 |
+
summary += f"*Note: The analysis is based on the {event_name} event.*\n\n"
|
| 256 |
+
|
| 257 |
+
return summary
|
| 258 |
+
|
| 259 |
|
|
|
|
| 260 |
with gr.Blocks() as app:
|
| 261 |
+
gr.Markdown("# 🚀 GitHub Starknet Developer Insights")
|
| 262 |
+
gr.Markdown(
|
| 263 |
+
"""
|
| 264 |
+
This tool allows you to analyze the GitHub activity of developers within the Starknet ecosystem.
|
| 265 |
+
Enter GitHub handles separated by commas or upload a CSV file with GitHub handles in a single column
|
| 266 |
+
to see their monthly commit activity, involvement classification, and comparisons with other developers.
|
| 267 |
+
"""
|
| 268 |
+
)
|
| 269 |
+
with gr.Row():
|
| 270 |
+
with gr.Column():
|
| 271 |
+
text_input = gr.Textbox(
|
| 272 |
+
label="Enter GitHub handles separated by commas",
|
| 273 |
+
placeholder="e.g., user1,user2,user3",
|
| 274 |
+
)
|
| 275 |
+
file_input = gr.File(
|
| 276 |
+
label="Or upload a CSV file with GitHub handles in a single column",
|
| 277 |
+
type="binary",
|
| 278 |
+
)
|
| 279 |
+
gr.Markdown(
|
| 280 |
+
"""
|
| 281 |
+
*Note:* When uploading a CSV, ensure it contains a single column of GitHub handles without a header row.
|
| 282 |
+
"""
|
| 283 |
+
)
|
| 284 |
+
with gr.Row():
|
| 285 |
+
program_end_date_input = gr.Textbox(label="Program End Date (YYYY-MM-DD)", placeholder="e.g., 2023-06-30")
|
| 286 |
+
event_name_input = gr.Textbox(label="Event Name (optional)", placeholder="e.g., Basecamp, Hackathon")
|
| 287 |
+
gr.Markdown(
|
| 288 |
+
"""
|
| 289 |
+
💡 *Tip: Specifying a program end date allows you to analyze the impact of events like Basecamp or Hackathons on developer activity. Leave it blank to analyze overall activity.*
|
| 290 |
+
"""
|
| 291 |
+
)
|
| 292 |
+
btn = gr.Button("Analyze")
|
| 293 |
+
|
| 294 |
+
with gr.Column():
|
| 295 |
+
tldr_output = gr.Markdown(label="📝 TLDR Summary")
|
| 296 |
+
|
| 297 |
with gr.Row():
|
| 298 |
+
with gr.Column():
|
| 299 |
+
plot_output = gr.Plot(label="📈 Commits per Month")
|
| 300 |
+
with gr.Column():
|
| 301 |
+
box_plot_output = gr.Plot(label="📊 Box Plot Comparison (Last 3 Months)")
|
| 302 |
+
|
| 303 |
+
with gr.Accordion("📊 Statistical Analysis", open=False):
|
| 304 |
+
stat_analysis_output = gr.Textbox(label="Statistical Analysis Results")
|
| 305 |
+
gr.Markdown(
|
| 306 |
+
"""
|
| 307 |
+
The Mann-Whitney U test is used to compare the commit activity of developers before and after the program.
|
| 308 |
+
- The test statistic measures the difference in the distribution of commits between the two groups (before and after).
|
| 309 |
+
- The p-value indicates the probability of observing such a difference by chance, assuming there is no real difference between the groups.
|
| 310 |
+
- A p-value less than 0.2 suggests that the difference is considered significant.
|
| 311 |
+
- A positive test statistic indicates that the commit activity is higher after the program, while a negative value indicates lower activity.
|
| 312 |
+
"""
|
| 313 |
+
)
|
| 314 |
+
|
| 315 |
+
with gr.Accordion("🆕 New Developers", open=False):
|
| 316 |
+
new_developers_output = gr.Textbox(label="Number of New Developers")
|
| 317 |
+
|
| 318 |
+
with gr.Accordion("🏆 Developer Classification", open=False):
|
| 319 |
+
table_output = gr.Dataframe(label="Developer Classification")
|
| 320 |
+
gr.Markdown(
|
| 321 |
+
"""
|
| 322 |
+
### Developer Classification Criteria
|
| 323 |
+
- **Always been inactive**: No commits have been recorded in the dataset.
|
| 324 |
+
- **Previously active but no longer**: Had commits earlier but none in the last 3 months.
|
| 325 |
+
- **Low-level active**: Fewer than 20 commits in the last 3 months.
|
| 326 |
+
- **Highly involved**: 20 or more commits in the last 3 months.
|
| 327 |
+
"""
|
| 328 |
+
)
|
| 329 |
|
| 330 |
+
with gr.Accordion("🔍 Comparison with Other Developers", open=False):
|
| 331 |
+
comparison_output = gr.Textbox(label="Comparison with Other Developers")
|
| 332 |
+
gr.Markdown(
|
| 333 |
+
"""
|
| 334 |
+
The Mann-Whitney U test is used to compare the commit activity of the user-specified developers with the rest of the developers in the database since the program end date.
|
| 335 |
+
- The test statistic measures the difference in the distribution of commits between the two groups.
|
| 336 |
+
- The p-value indicates the probability of observing such a difference by chance, assuming there is no real difference between the groups.
|
| 337 |
+
- A p-value less than 0.25 suggests that the difference is considered significant.
|
| 338 |
+
- If the test statistic is positive, it means the user-specified developers have a higher number of commits compared to other developers, and vice versa.
|
| 339 |
+
"""
|
| 340 |
+
)
|
| 341 |
|
| 342 |
+
with gr.Accordion("📈 Growth Rate Comparison", open=False):
|
| 343 |
+
growth_rate_output = gr.Textbox(label="Growth Rate Comparison")
|
| 344 |
+
gr.Markdown(
|
| 345 |
+
"""
|
| 346 |
+
The average growth rate of commit activity is compared between the user-specified developers and other developers.
|
| 347 |
+
- The growth rate is calculated as the relative change in the number of commits from one month to the next.
|
| 348 |
+
- The Mann-Whitney U test is used to compare the average growth rates between the two groups.
|
| 349 |
+
- A p-value less than 0.25 suggests that the difference in average growth rates is statistically significant.
|
| 350 |
+
- If the test statistic is positive, it means the user-specified developers have a higher average growth rate compared to other developers, and vice versa.
|
| 351 |
+
"""
|
| 352 |
+
)
|
| 353 |
+
|
| 354 |
+
gr.Markdown(
|
| 355 |
+
"""
|
| 356 |
+
💡 *Disclaimer: This information is only for open-source repos and should be taken with a grain of salt. Commits in certain repos may be more important than others, and there are many private repos from several teams that are not included in this analysis.*
|
| 357 |
+
"""
|
| 358 |
+
)
|
| 359 |
+
|
| 360 |
+
btn.click(
|
| 361 |
+
process_input,
|
| 362 |
+
inputs=[text_input, file_input, program_end_date_input, event_name_input],
|
| 363 |
+
outputs=[plot_output, box_plot_output, table_output, stat_analysis_output, new_developers_output, comparison_output, growth_rate_output, tldr_output],
|
| 364 |
+
)
|
| 365 |
+
|
| 366 |
+
print(colored("Gradio app initialized.", "blue"))
|
| 367 |
|
| 368 |
if __name__ == "__main__":
|
| 369 |
print(colored("Launching app...", "blue"))
|
| 370 |
+
app.launch(share=True)
|
|
|
github_metrics/utils.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import datetime
|
| 2 |
+
from termcolor import colored
|
| 3 |
+
import pandas as pd
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def load_all_developers_dataset():
|
| 7 |
+
try:
|
| 8 |
+
print(colored("Loading dataset...", "blue"))
|
| 9 |
+
df = pd.read_csv("data/source/all_networks_developer_classification.csv")
|
| 10 |
+
df["month_year"] = pd.to_datetime(df["month_year"], format="%B_%Y")
|
| 11 |
+
return df
|
| 12 |
+
except Exception as e:
|
| 13 |
+
print(colored(f"Error loading dataset: {e}", "red"))
|
| 14 |
+
raise
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def save_plot(plt, base_filename):
|
| 18 |
+
"""
|
| 19 |
+
Save a matplotlib plot to a file with a timestamped filename.
|
| 20 |
+
"""
|
| 21 |
+
current_date = datetime.now().strftime("%Y-%m-%d")
|
| 22 |
+
filename = f"data/figures/{base_filename}_{current_date}.png"
|
| 23 |
+
plt.savefig(filename, dpi=300, bbox_inches="tight")
|
| 24 |
+
plt.close()
|
poetry.lock
CHANGED
|
@@ -84,6 +84,35 @@ tests = ["attrs[tests-no-zope]", "zope-interface"]
|
|
| 84 |
tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
|
| 85 |
tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
|
| 86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
[[package]]
|
| 88 |
name = "certifi"
|
| 89 |
version = "2024.2.2"
|
|
@@ -407,6 +436,29 @@ ufo = ["fs (>=2.2.0,<3)"]
|
|
| 407 |
unicode = ["unicodedata2 (>=15.1.0)"]
|
| 408 |
woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
|
| 409 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
[[package]]
|
| 411 |
name = "fsspec"
|
| 412 |
version = "2024.2.0"
|
|
@@ -442,6 +494,17 @@ smb = ["smbprotocol"]
|
|
| 442 |
ssh = ["paramiko"]
|
| 443 |
tqdm = ["tqdm"]
|
| 444 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
[[package]]
|
| 446 |
name = "gradio"
|
| 447 |
version = "4.19.2"
|
|
@@ -484,6 +547,23 @@ uvicorn = ">=0.14.0"
|
|
| 484 |
[package.extras]
|
| 485 |
oauth = ["authlib", "itsdangerous"]
|
| 486 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 487 |
[[package]]
|
| 488 |
name = "gradio-client"
|
| 489 |
version = "0.10.1"
|
|
@@ -617,6 +697,17 @@ files = [
|
|
| 617 |
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
|
| 618 |
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"]
|
| 619 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 620 |
[[package]]
|
| 621 |
name = "jinja2"
|
| 622 |
version = "3.1.3"
|
|
@@ -782,6 +873,26 @@ files = [
|
|
| 782 |
{file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"},
|
| 783 |
]
|
| 784 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 785 |
[[package]]
|
| 786 |
name = "markdown-it-py"
|
| 787 |
version = "3.0.0"
|
|
@@ -1206,6 +1317,21 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
|
|
| 1206 |
typing = ["typing-extensions"]
|
| 1207 |
xmp = ["defusedxml"]
|
| 1208 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1209 |
[[package]]
|
| 1210 |
name = "pydantic"
|
| 1211 |
version = "2.6.2"
|
|
@@ -1642,6 +1768,69 @@ files = [
|
|
| 1642 |
{file = "ruff-0.2.2.tar.gz", hash = "sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d"},
|
| 1643 |
]
|
| 1644 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1645 |
[[package]]
|
| 1646 |
name = "semantic-version"
|
| 1647 |
version = "2.10.0"
|
|
@@ -1707,6 +1896,20 @@ anyio = ">=3.4.0,<5"
|
|
| 1707 |
[package.extras]
|
| 1708 |
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
|
| 1709 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1710 |
[[package]]
|
| 1711 |
name = "termcolor"
|
| 1712 |
version = "2.4.0"
|
|
@@ -1923,7 +2126,86 @@ files = [
|
|
| 1923 |
{file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"},
|
| 1924 |
]
|
| 1925 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1926 |
[metadata]
|
| 1927 |
lock-version = "2.0"
|
| 1928 |
python-versions = "^3.11"
|
| 1929 |
-
content-hash = "
|
|
|
|
| 84 |
tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
|
| 85 |
tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
|
| 86 |
|
| 87 |
+
[[package]]
|
| 88 |
+
name = "autograd"
|
| 89 |
+
version = "1.6.2"
|
| 90 |
+
description = "Efficiently computes derivatives of numpy code."
|
| 91 |
+
optional = false
|
| 92 |
+
python-versions = "*"
|
| 93 |
+
files = [
|
| 94 |
+
{file = "autograd-1.6.2-py3-none-any.whl", hash = "sha256:208dde2a938e63b4f8f5049b1985505139e529068b0d26f8cd7771fd3eb145d5"},
|
| 95 |
+
{file = "autograd-1.6.2.tar.gz", hash = "sha256:8731e08a0c4e389d8695a40072ada4512641c113b6cace8f4cfbe8eb7e9aedeb"},
|
| 96 |
+
]
|
| 97 |
+
|
| 98 |
+
[package.dependencies]
|
| 99 |
+
future = ">=0.15.2"
|
| 100 |
+
numpy = ">=1.12"
|
| 101 |
+
|
| 102 |
+
[[package]]
|
| 103 |
+
name = "autograd-gamma"
|
| 104 |
+
version = "0.5.0"
|
| 105 |
+
description = "Autograd compatible approximations to the gamma family of functions"
|
| 106 |
+
optional = false
|
| 107 |
+
python-versions = "*"
|
| 108 |
+
files = [
|
| 109 |
+
{file = "autograd-gamma-0.5.0.tar.gz", hash = "sha256:f27abb7b8bb9cffc8badcbf59f3fe44a9db39e124ecacf1992b6d952934ac9c4"},
|
| 110 |
+
]
|
| 111 |
+
|
| 112 |
+
[package.dependencies]
|
| 113 |
+
autograd = ">=1.2.0"
|
| 114 |
+
scipy = ">=1.2.0"
|
| 115 |
+
|
| 116 |
[[package]]
|
| 117 |
name = "certifi"
|
| 118 |
version = "2024.2.2"
|
|
|
|
| 436 |
unicode = ["unicodedata2 (>=15.1.0)"]
|
| 437 |
woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
|
| 438 |
|
| 439 |
+
[[package]]
|
| 440 |
+
name = "formulaic"
|
| 441 |
+
version = "1.0.1"
|
| 442 |
+
description = "An implementation of Wilkinson formulas."
|
| 443 |
+
optional = false
|
| 444 |
+
python-versions = ">=3.7.2"
|
| 445 |
+
files = [
|
| 446 |
+
{file = "formulaic-1.0.1-py3-none-any.whl", hash = "sha256:be9e6127b98ba0293654be528fd070ede264f64d0247477c4d5af799b0b12cf2"},
|
| 447 |
+
{file = "formulaic-1.0.1.tar.gz", hash = "sha256:64dd7992a7aa5bbceb1e40679d0f01fc6f0ba12b7d23d78094a88c2edc68fba1"},
|
| 448 |
+
]
|
| 449 |
+
|
| 450 |
+
[package.dependencies]
|
| 451 |
+
interface-meta = ">=1.2.0"
|
| 452 |
+
numpy = ">=1.16.5"
|
| 453 |
+
pandas = ">=1.0"
|
| 454 |
+
scipy = ">=1.6"
|
| 455 |
+
typing-extensions = ">=4.2.0"
|
| 456 |
+
wrapt = ">=1.0"
|
| 457 |
+
|
| 458 |
+
[package.extras]
|
| 459 |
+
arrow = ["pyarrow (>=1)"]
|
| 460 |
+
calculus = ["sympy (>=1.3,!=1.10)"]
|
| 461 |
+
|
| 462 |
[[package]]
|
| 463 |
name = "fsspec"
|
| 464 |
version = "2024.2.0"
|
|
|
|
| 494 |
ssh = ["paramiko"]
|
| 495 |
tqdm = ["tqdm"]
|
| 496 |
|
| 497 |
+
[[package]]
|
| 498 |
+
name = "future"
|
| 499 |
+
version = "1.0.0"
|
| 500 |
+
description = "Clean single-source support for Python 3 and 2"
|
| 501 |
+
optional = false
|
| 502 |
+
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
| 503 |
+
files = [
|
| 504 |
+
{file = "future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216"},
|
| 505 |
+
{file = "future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05"},
|
| 506 |
+
]
|
| 507 |
+
|
| 508 |
[[package]]
|
| 509 |
name = "gradio"
|
| 510 |
version = "4.19.2"
|
|
|
|
| 547 |
[package.extras]
|
| 548 |
oauth = ["authlib", "itsdangerous"]
|
| 549 |
|
| 550 |
+
[[package]]
|
| 551 |
+
name = "gradio-calendar"
|
| 552 |
+
version = "0.0.4"
|
| 553 |
+
description = "Gradio component for selecting dates with a calendar 📆"
|
| 554 |
+
optional = false
|
| 555 |
+
python-versions = ">=3.8"
|
| 556 |
+
files = [
|
| 557 |
+
{file = "gradio_calendar-0.0.4-py3-none-any.whl", hash = "sha256:32f738ea94ed4dea1e4069a117d907fc3e8a2757fb2f4b0ce67a801f434928df"},
|
| 558 |
+
{file = "gradio_calendar-0.0.4.tar.gz", hash = "sha256:e4ea93cc7f6284bb8e547bb34d891776f1f0bebd320eae80bd644c0cd30694ce"},
|
| 559 |
+
]
|
| 560 |
+
|
| 561 |
+
[package.dependencies]
|
| 562 |
+
gradio = ">=4.0,<5.0"
|
| 563 |
+
|
| 564 |
+
[package.extras]
|
| 565 |
+
dev = ["build", "twine"]
|
| 566 |
+
|
| 567 |
[[package]]
|
| 568 |
name = "gradio-client"
|
| 569 |
version = "0.10.1"
|
|
|
|
| 697 |
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
|
| 698 |
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"]
|
| 699 |
|
| 700 |
+
[[package]]
|
| 701 |
+
name = "interface-meta"
|
| 702 |
+
version = "1.3.0"
|
| 703 |
+
description = "`interface_meta` provides a convenient way to expose an extensible API with enforced method signatures and consistent documentation."
|
| 704 |
+
optional = false
|
| 705 |
+
python-versions = ">=3.7,<4.0"
|
| 706 |
+
files = [
|
| 707 |
+
{file = "interface_meta-1.3.0-py3-none-any.whl", hash = "sha256:de35dc5241431886e709e20a14d6597ed07c9f1e8b4bfcffde2190ca5b700ee8"},
|
| 708 |
+
{file = "interface_meta-1.3.0.tar.gz", hash = "sha256:8a4493f8bdb73fb9655dcd5115bc897e207319e36c8835f39c516a2d7e9d79a1"},
|
| 709 |
+
]
|
| 710 |
+
|
| 711 |
[[package]]
|
| 712 |
name = "jinja2"
|
| 713 |
version = "3.1.3"
|
|
|
|
| 873 |
{file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"},
|
| 874 |
]
|
| 875 |
|
| 876 |
+
[[package]]
|
| 877 |
+
name = "lifelines"
|
| 878 |
+
version = "0.28.0"
|
| 879 |
+
description = "Survival analysis in Python, including Kaplan Meier, Nelson Aalen and regression"
|
| 880 |
+
optional = false
|
| 881 |
+
python-versions = ">=3.9"
|
| 882 |
+
files = [
|
| 883 |
+
{file = "lifelines-0.28.0-py3-none-any.whl", hash = "sha256:eacade0527a62d6086cb76f7dbf1e94f2f445a696dd7168723bc7a86e4737a99"},
|
| 884 |
+
{file = "lifelines-0.28.0.tar.gz", hash = "sha256:eecf726453fd409c94fef8a521f8e593bcd09337f920fe885131f01cfe58b25e"},
|
| 885 |
+
]
|
| 886 |
+
|
| 887 |
+
[package.dependencies]
|
| 888 |
+
autograd = ">=1.5"
|
| 889 |
+
autograd-gamma = ">=0.3"
|
| 890 |
+
formulaic = ">=0.2.2"
|
| 891 |
+
matplotlib = ">=3.0"
|
| 892 |
+
numpy = ">=1.14.0,<2.0"
|
| 893 |
+
pandas = ">=1.2.0"
|
| 894 |
+
scipy = ">=1.2.0"
|
| 895 |
+
|
| 896 |
[[package]]
|
| 897 |
name = "markdown-it-py"
|
| 898 |
version = "3.0.0"
|
|
|
|
| 1317 |
typing = ["typing-extensions"]
|
| 1318 |
xmp = ["defusedxml"]
|
| 1319 |
|
| 1320 |
+
[[package]]
|
| 1321 |
+
name = "plotly"
|
| 1322 |
+
version = "5.19.0"
|
| 1323 |
+
description = "An open-source, interactive data visualization library for Python"
|
| 1324 |
+
optional = false
|
| 1325 |
+
python-versions = ">=3.8"
|
| 1326 |
+
files = [
|
| 1327 |
+
{file = "plotly-5.19.0-py3-none-any.whl", hash = "sha256:906abcc5f15945765328c5d47edaa884bc99f5985fbc61e8cd4dc361f4ff8f5a"},
|
| 1328 |
+
{file = "plotly-5.19.0.tar.gz", hash = "sha256:5ea91a56571292ade3e3bc9bf712eba0b95a1fb0a941375d978cc79432e055f4"},
|
| 1329 |
+
]
|
| 1330 |
+
|
| 1331 |
+
[package.dependencies]
|
| 1332 |
+
packaging = "*"
|
| 1333 |
+
tenacity = ">=6.2.0"
|
| 1334 |
+
|
| 1335 |
[[package]]
|
| 1336 |
name = "pydantic"
|
| 1337 |
version = "2.6.2"
|
|
|
|
| 1768 |
{file = "ruff-0.2.2.tar.gz", hash = "sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d"},
|
| 1769 |
]
|
| 1770 |
|
| 1771 |
+
[[package]]
|
| 1772 |
+
name = "scipy"
|
| 1773 |
+
version = "1.12.0"
|
| 1774 |
+
description = "Fundamental algorithms for scientific computing in Python"
|
| 1775 |
+
optional = false
|
| 1776 |
+
python-versions = ">=3.9"
|
| 1777 |
+
files = [
|
| 1778 |
+
{file = "scipy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:78e4402e140879387187f7f25d91cc592b3501a2e51dfb320f48dfb73565f10b"},
|
| 1779 |
+
{file = "scipy-1.12.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f5f00ebaf8de24d14b8449981a2842d404152774c1a1d880c901bf454cb8e2a1"},
|
| 1780 |
+
{file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e53958531a7c695ff66c2e7bb7b79560ffdc562e2051644c5576c39ff8efb563"},
|
| 1781 |
+
{file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e32847e08da8d895ce09d108a494d9eb78974cf6de23063f93306a3e419960c"},
|
| 1782 |
+
{file = "scipy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c1020cad92772bf44b8e4cdabc1df5d87376cb219742549ef69fc9fd86282dd"},
|
| 1783 |
+
{file = "scipy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:75ea2a144096b5e39402e2ff53a36fecfd3b960d786b7efd3c180e29c39e53f2"},
|
| 1784 |
+
{file = "scipy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:408c68423f9de16cb9e602528be4ce0d6312b05001f3de61fe9ec8b1263cad08"},
|
| 1785 |
+
{file = "scipy-1.12.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5adfad5dbf0163397beb4aca679187d24aec085343755fcdbdeb32b3679f254c"},
|
| 1786 |
+
{file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3003652496f6e7c387b1cf63f4bb720951cfa18907e998ea551e6de51a04467"},
|
| 1787 |
+
{file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b8066bce124ee5531d12a74b617d9ac0ea59245246410e19bca549656d9a40a"},
|
| 1788 |
+
{file = "scipy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8bee4993817e204d761dba10dbab0774ba5a8612e57e81319ea04d84945375ba"},
|
| 1789 |
+
{file = "scipy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a24024d45ce9a675c1fb8494e8e5244efea1c7a09c60beb1eeb80373d0fecc70"},
|
| 1790 |
+
{file = "scipy-1.12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e7e76cc48638228212c747ada851ef355c2bb5e7f939e10952bc504c11f4e372"},
|
| 1791 |
+
{file = "scipy-1.12.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f7ce148dffcd64ade37b2df9315541f9adad6efcaa86866ee7dd5db0c8f041c3"},
|
| 1792 |
+
{file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c39f92041f490422924dfdb782527a4abddf4707616e07b021de33467f917bc"},
|
| 1793 |
+
{file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ebda398f86e56178c2fa94cad15bf457a218a54a35c2a7b4490b9f9cb2676c"},
|
| 1794 |
+
{file = "scipy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:95e5c750d55cf518c398a8240571b0e0782c2d5a703250872f36eaf737751338"},
|
| 1795 |
+
{file = "scipy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e646d8571804a304e1da01040d21577685ce8e2db08ac58e543eaca063453e1c"},
|
| 1796 |
+
{file = "scipy-1.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:913d6e7956c3a671de3b05ccb66b11bc293f56bfdef040583a7221d9e22a2e35"},
|
| 1797 |
+
{file = "scipy-1.12.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba1b0c7256ad75401c73e4b3cf09d1f176e9bd4248f0d3112170fb2ec4db067"},
|
| 1798 |
+
{file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:730badef9b827b368f351eacae2e82da414e13cf8bd5051b4bdfd720271a5371"},
|
| 1799 |
+
{file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6546dc2c11a9df6926afcbdd8a3edec28566e4e785b915e849348c6dd9f3f490"},
|
| 1800 |
+
{file = "scipy-1.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:196ebad3a4882081f62a5bf4aeb7326aa34b110e533aab23e4374fcccb0890dc"},
|
| 1801 |
+
{file = "scipy-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:b360f1b6b2f742781299514e99ff560d1fe9bd1bff2712894b52abe528d1fd1e"},
|
| 1802 |
+
{file = "scipy-1.12.0.tar.gz", hash = "sha256:4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3"},
|
| 1803 |
+
]
|
| 1804 |
+
|
| 1805 |
+
[package.dependencies]
|
| 1806 |
+
numpy = ">=1.22.4,<1.29.0"
|
| 1807 |
+
|
| 1808 |
+
[package.extras]
|
| 1809 |
+
dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"]
|
| 1810 |
+
doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"]
|
| 1811 |
+
test = ["asv", "gmpy2", "hypothesis", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
|
| 1812 |
+
|
| 1813 |
+
[[package]]
|
| 1814 |
+
name = "seaborn"
|
| 1815 |
+
version = "0.13.2"
|
| 1816 |
+
description = "Statistical data visualization"
|
| 1817 |
+
optional = false
|
| 1818 |
+
python-versions = ">=3.8"
|
| 1819 |
+
files = [
|
| 1820 |
+
{file = "seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987"},
|
| 1821 |
+
{file = "seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7"},
|
| 1822 |
+
]
|
| 1823 |
+
|
| 1824 |
+
[package.dependencies]
|
| 1825 |
+
matplotlib = ">=3.4,<3.6.1 || >3.6.1"
|
| 1826 |
+
numpy = ">=1.20,<1.24.0 || >1.24.0"
|
| 1827 |
+
pandas = ">=1.2"
|
| 1828 |
+
|
| 1829 |
+
[package.extras]
|
| 1830 |
+
dev = ["flake8", "flit", "mypy", "pandas-stubs", "pre-commit", "pytest", "pytest-cov", "pytest-xdist"]
|
| 1831 |
+
docs = ["ipykernel", "nbconvert", "numpydoc", "pydata_sphinx_theme (==0.10.0rc2)", "pyyaml", "sphinx (<6.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-issues"]
|
| 1832 |
+
stats = ["scipy (>=1.7)", "statsmodels (>=0.12)"]
|
| 1833 |
+
|
| 1834 |
[[package]]
|
| 1835 |
name = "semantic-version"
|
| 1836 |
version = "2.10.0"
|
|
|
|
| 1896 |
[package.extras]
|
| 1897 |
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
|
| 1898 |
|
| 1899 |
+
[[package]]
|
| 1900 |
+
name = "tenacity"
|
| 1901 |
+
version = "8.2.3"
|
| 1902 |
+
description = "Retry code until it succeeds"
|
| 1903 |
+
optional = false
|
| 1904 |
+
python-versions = ">=3.7"
|
| 1905 |
+
files = [
|
| 1906 |
+
{file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"},
|
| 1907 |
+
{file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"},
|
| 1908 |
+
]
|
| 1909 |
+
|
| 1910 |
+
[package.extras]
|
| 1911 |
+
doc = ["reno", "sphinx", "tornado (>=4.5)"]
|
| 1912 |
+
|
| 1913 |
[[package]]
|
| 1914 |
name = "termcolor"
|
| 1915 |
version = "2.4.0"
|
|
|
|
| 2126 |
{file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"},
|
| 2127 |
]
|
| 2128 |
|
| 2129 |
+
[[package]]
|
| 2130 |
+
name = "wrapt"
|
| 2131 |
+
version = "1.16.0"
|
| 2132 |
+
description = "Module for decorators, wrappers and monkey patching."
|
| 2133 |
+
optional = false
|
| 2134 |
+
python-versions = ">=3.6"
|
| 2135 |
+
files = [
|
| 2136 |
+
{file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
|
| 2137 |
+
{file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
|
| 2138 |
+
{file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"},
|
| 2139 |
+
{file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"},
|
| 2140 |
+
{file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"},
|
| 2141 |
+
{file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"},
|
| 2142 |
+
{file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"},
|
| 2143 |
+
{file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"},
|
| 2144 |
+
{file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"},
|
| 2145 |
+
{file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"},
|
| 2146 |
+
{file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"},
|
| 2147 |
+
{file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"},
|
| 2148 |
+
{file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"},
|
| 2149 |
+
{file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"},
|
| 2150 |
+
{file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"},
|
| 2151 |
+
{file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"},
|
| 2152 |
+
{file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"},
|
| 2153 |
+
{file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"},
|
| 2154 |
+
{file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"},
|
| 2155 |
+
{file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"},
|
| 2156 |
+
{file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"},
|
| 2157 |
+
{file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"},
|
| 2158 |
+
{file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"},
|
| 2159 |
+
{file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"},
|
| 2160 |
+
{file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"},
|
| 2161 |
+
{file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"},
|
| 2162 |
+
{file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"},
|
| 2163 |
+
{file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"},
|
| 2164 |
+
{file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"},
|
| 2165 |
+
{file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"},
|
| 2166 |
+
{file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"},
|
| 2167 |
+
{file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"},
|
| 2168 |
+
{file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"},
|
| 2169 |
+
{file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"},
|
| 2170 |
+
{file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"},
|
| 2171 |
+
{file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"},
|
| 2172 |
+
{file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"},
|
| 2173 |
+
{file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"},
|
| 2174 |
+
{file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"},
|
| 2175 |
+
{file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"},
|
| 2176 |
+
{file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"},
|
| 2177 |
+
{file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"},
|
| 2178 |
+
{file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"},
|
| 2179 |
+
{file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"},
|
| 2180 |
+
{file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"},
|
| 2181 |
+
{file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"},
|
| 2182 |
+
{file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"},
|
| 2183 |
+
{file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"},
|
| 2184 |
+
{file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"},
|
| 2185 |
+
{file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"},
|
| 2186 |
+
{file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"},
|
| 2187 |
+
{file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"},
|
| 2188 |
+
{file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"},
|
| 2189 |
+
{file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"},
|
| 2190 |
+
{file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"},
|
| 2191 |
+
{file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"},
|
| 2192 |
+
{file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"},
|
| 2193 |
+
{file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"},
|
| 2194 |
+
{file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"},
|
| 2195 |
+
{file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"},
|
| 2196 |
+
{file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"},
|
| 2197 |
+
{file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"},
|
| 2198 |
+
{file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"},
|
| 2199 |
+
{file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"},
|
| 2200 |
+
{file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"},
|
| 2201 |
+
{file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"},
|
| 2202 |
+
{file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"},
|
| 2203 |
+
{file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"},
|
| 2204 |
+
{file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
|
| 2205 |
+
{file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
|
| 2206 |
+
]
|
| 2207 |
+
|
| 2208 |
[metadata]
|
| 2209 |
lock-version = "2.0"
|
| 2210 |
python-versions = "^3.11"
|
| 2211 |
+
content-hash = "93c07a0e554c6697440ab4476e4eaa56b3e41e1d857b3052df1d13e27ba77c4c"
|
pyproject.toml
CHANGED
|
@@ -12,6 +12,12 @@ gradio = "^4.19.2"
|
|
| 12 |
pandas = "^2.2.1"
|
| 13 |
matplotlib = "^3.8.3"
|
| 14 |
termcolor = "^2.4.0"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
|
| 17 |
[build-system]
|
|
|
|
| 12 |
pandas = "^2.2.1"
|
| 13 |
matplotlib = "^3.8.3"
|
| 14 |
termcolor = "^2.4.0"
|
| 15 |
+
seaborn = "^0.13.2"
|
| 16 |
+
numpy = "^1.26.4"
|
| 17 |
+
lifelines = "^0.28.0"
|
| 18 |
+
plotly = "^5.19.0"
|
| 19 |
+
gradio-calendar = "^0.0.4"
|
| 20 |
+
scipy = "^1.12.0"
|
| 21 |
|
| 22 |
|
| 23 |
[build-system]
|