population-trend 2.0.2__tar.gz → 3.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {population_trend-2.0.2 → population_trend-3.1.0}/PKG-INFO +2 -2
- {population_trend-2.0.2 → population_trend-3.1.0}/population_trend/__init__.py +3 -1
- population_trend-3.1.0/population_trend/calculate_growth_rates.py +206 -0
- {population_trend-2.0.2 → population_trend-3.1.0}/population_trend/cli.py +4 -4
- {population_trend-2.0.2 → population_trend-3.1.0}/population_trend/population_growth_model.py +27 -22
- {population_trend-2.0.2 → population_trend-3.1.0}/pyproject.toml +1 -1
- {population_trend-2.0.2 → population_trend-3.1.0}/README.md +0 -0
- {population_trend-2.0.2 → population_trend-3.1.0}/population_trend/filter_data.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: population_trend
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: A template Python module
|
|
5
5
|
Home-page: https://github.com/IslasGECI/population_trend
|
|
6
6
|
Author: Ciencia de Datos • GECI
|
|
@@ -8,7 +8,7 @@ Author-email: ciencia.datos@islas.org.mx
|
|
|
8
8
|
Requires-Python: >=3.9
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
11
|
-
Requires-Dist: bootstrapping-tools==
|
|
11
|
+
Requires-Dist: bootstrapping-tools==2.0.0
|
|
12
12
|
Requires-Dist: geci-plots
|
|
13
13
|
Requires-Dist: typer
|
|
14
14
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""A template Python module"""
|
|
2
2
|
|
|
3
|
-
__version__ = "
|
|
3
|
+
__version__ = "3.1.0"
|
|
4
4
|
from .cli import * # noqa
|
|
5
5
|
from .filter_data import * # noqa
|
|
6
6
|
from .population_growth_model import * # noqa
|
|
7
|
+
from .calculate_growth_rates import * # noqa
|
|
8
|
+
from bootstrapping_tools import * # noqa
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
from bootstrapping_tools import (
|
|
2
|
+
lambda_calculator,
|
|
3
|
+
power_law,
|
|
4
|
+
bootstrap_from_time_series,
|
|
5
|
+
get_bootstrap_deltas,
|
|
6
|
+
generate_latex_interval_string,
|
|
7
|
+
calculate_p_values,
|
|
8
|
+
)
|
|
9
|
+
from matplotlib.ticker import MaxNLocator
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def do_nothing():
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def fit_population_model(seasons_series, data_series):
|
|
21
|
+
parameters = lambda_calculator(seasons_series, data_series)
|
|
22
|
+
model = power_law(
|
|
23
|
+
seasons_series - seasons_series.iloc[0],
|
|
24
|
+
parameters[0],
|
|
25
|
+
parameters[1],
|
|
26
|
+
)
|
|
27
|
+
return model
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def calculate_porcentual_diff(x, y):
|
|
31
|
+
return 100 * (x - y) / y
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def generate_number_and_season_string(number, season):
|
|
35
|
+
return "{} ({})".format(
|
|
36
|
+
number,
|
|
37
|
+
season,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def calculate_seasons_intervals(seasons):
|
|
42
|
+
years = []
|
|
43
|
+
first_index = 0
|
|
44
|
+
for index in np.where(np.diff(seasons) != 1)[0]:
|
|
45
|
+
if seasons[first_index] == seasons[index]:
|
|
46
|
+
years.append(f"{seasons[index]}")
|
|
47
|
+
else:
|
|
48
|
+
years.append(f"{seasons[first_index]}-{seasons[index]}")
|
|
49
|
+
first_index = index + 1
|
|
50
|
+
years.append(f"{seasons[first_index]}-{seasons[-1]}")
|
|
51
|
+
return years
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def calculate_interest_numbers(cantidad_nidos, model):
|
|
55
|
+
first_number = generate_number_and_season_string(
|
|
56
|
+
cantidad_nidos["Maxima_cantidad_nidos"].iloc[0],
|
|
57
|
+
cantidad_nidos["Temporada"].iloc[0],
|
|
58
|
+
)
|
|
59
|
+
first_number_calculated = generate_number_and_season_string(
|
|
60
|
+
model.iloc[0], cantidad_nidos["Temporada"].iloc[0]
|
|
61
|
+
)
|
|
62
|
+
last_number = "{{{:,.0f}}} ({})".format(
|
|
63
|
+
cantidad_nidos["Maxima_cantidad_nidos"].iloc[-1],
|
|
64
|
+
int(cantidad_nidos["Temporada"].iloc[-1]),
|
|
65
|
+
)
|
|
66
|
+
last_number_calculated = generate_number_and_season_string(
|
|
67
|
+
model.iloc[-1],
|
|
68
|
+
cantidad_nidos["Temporada"].iloc[-1],
|
|
69
|
+
)
|
|
70
|
+
return first_number, first_number_calculated, last_number, last_number_calculated
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def calculate_percent_diff_in_seasons(cantidad_nidos, model):
|
|
74
|
+
if cantidad_nidos["Maxima_cantidad_nidos"].iloc[0] == 0:
|
|
75
|
+
porcentaje_cambio = calculate_porcentual_diff(model.iloc[-1], model.iloc[0])
|
|
76
|
+
else:
|
|
77
|
+
porcentaje_cambio = calculate_porcentual_diff(
|
|
78
|
+
model.iloc[-1], cantidad_nidos["Maxima_cantidad_nidos"].iloc[0]
|
|
79
|
+
)
|
|
80
|
+
return porcentaje_cambio
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Bootstrap_from_time_series_parameterizer:
|
|
84
|
+
def __init__(self, blocks_length=3, N=2000):
|
|
85
|
+
self.parameters = dict(
|
|
86
|
+
dataframe=None,
|
|
87
|
+
column_name="Maxima_cantidad_nidos",
|
|
88
|
+
N=N,
|
|
89
|
+
return_distribution=True,
|
|
90
|
+
blocks_length=blocks_length,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def set_data(self, data):
|
|
94
|
+
self.parameters["dataframe"] = data
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
Bootstrap = dict(
|
|
98
|
+
default=Bootstrap_from_time_series_parameterizer(),
|
|
99
|
+
testing=Bootstrap_from_time_series_parameterizer(blocks_length=2, N=100),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class Bootstrap_from_time_series:
|
|
104
|
+
def __init__(self, bootstrap_parametrizer):
|
|
105
|
+
self.parameters = bootstrap_parametrizer.parameters
|
|
106
|
+
self.lambdas_distribution, self.intervals = self._calculate_distribution_and_interval()
|
|
107
|
+
self.season_series = self.parameters["dataframe"]["Temporada"]
|
|
108
|
+
self.data_series = self.parameters["dataframe"][self.parameters["column_name"]]
|
|
109
|
+
self.lambdas = [interval[0] for interval in self.intervals]
|
|
110
|
+
|
|
111
|
+
def get_distribution(self):
|
|
112
|
+
return self.lambdas_distribution
|
|
113
|
+
|
|
114
|
+
def _calculate_distribution_and_interval(self):
|
|
115
|
+
lambdas_distribution, intervals = bootstrap_from_time_series(**self.parameters)
|
|
116
|
+
return lambdas_distribution, intervals
|
|
117
|
+
|
|
118
|
+
def get_inferior_central_and_superior_limit(self):
|
|
119
|
+
inferior_limit, central, superior_limit = get_bootstrap_deltas(
|
|
120
|
+
self.lambdas, **{"decimals": 2}
|
|
121
|
+
)
|
|
122
|
+
return inferior_limit, central, superior_limit
|
|
123
|
+
|
|
124
|
+
def get_lambda_interval_latex_string(self):
|
|
125
|
+
lambda_latex_string = generate_latex_interval_string(self.lambdas, **{"decimals": 2})
|
|
126
|
+
return lambda_latex_string
|
|
127
|
+
|
|
128
|
+
def generate_season_interval(self):
|
|
129
|
+
return "({}-{})".format(
|
|
130
|
+
self.season_series.min(axis=0),
|
|
131
|
+
self.season_series.max(axis=0),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def get_monitored_seasons(self):
|
|
135
|
+
monitored_seasons = np.sort(self.season_series.astype(int).unique())
|
|
136
|
+
if len(monitored_seasons) == 1:
|
|
137
|
+
return f"{monitored_seasons[0]}"
|
|
138
|
+
else:
|
|
139
|
+
seasons_intervals = calculate_seasons_intervals(monitored_seasons)
|
|
140
|
+
return ",".join(seasons_intervals)
|
|
141
|
+
|
|
142
|
+
def xxfit_population_model(self):
|
|
143
|
+
parameters = lambda_calculator(self.season_series, self.data_series)
|
|
144
|
+
model = power_law(
|
|
145
|
+
self.season_series - self.season_series.iloc[0],
|
|
146
|
+
parameters[0],
|
|
147
|
+
parameters[1],
|
|
148
|
+
)
|
|
149
|
+
return model
|
|
150
|
+
|
|
151
|
+
def save_intervals(self, output_path):
|
|
152
|
+
json_dict = {
|
|
153
|
+
"intervals": list(self.intervals),
|
|
154
|
+
"lambda_latex_interval": self.get_lambda_interval_latex_string(),
|
|
155
|
+
}
|
|
156
|
+
with open(output_path, "w") as file:
|
|
157
|
+
json.dump(json_dict, file)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def calculate_growth_rates_table(bootstrap: Bootstrap_from_time_series_parameterizer):
|
|
161
|
+
bootstraper = Bootstrap_from_time_series(bootstrap)
|
|
162
|
+
df = bootstrap.parameters["dataframe"]
|
|
163
|
+
model = bootstraper.xxfit_population_model()
|
|
164
|
+
inferior_limit, central, superior_limit = bootstraper.get_inferior_central_and_superior_limit()
|
|
165
|
+
lambda_latex_string = bootstraper.get_lambda_interval_latex_string()
|
|
166
|
+
bootstrap_distribution = bootstraper.get_distribution()
|
|
167
|
+
lambdas_distribution = [interval[0] for interval in bootstrap_distribution]
|
|
168
|
+
p_value_mayor, p_value_menor = calculate_p_values(lambdas_distribution)
|
|
169
|
+
intervalo = bootstraper.generate_season_interval()
|
|
170
|
+
monitored_seasons = bootstraper.get_monitored_seasons()
|
|
171
|
+
porcentaje_cambio = calculate_percent_diff_in_seasons(df, model)
|
|
172
|
+
(
|
|
173
|
+
first_number,
|
|
174
|
+
_,
|
|
175
|
+
last_number,
|
|
176
|
+
last_number_calculated,
|
|
177
|
+
) = calculate_interest_numbers(df, model)
|
|
178
|
+
return [
|
|
179
|
+
intervalo,
|
|
180
|
+
first_number,
|
|
181
|
+
last_number,
|
|
182
|
+
last_number_calculated,
|
|
183
|
+
lambda_latex_string,
|
|
184
|
+
df["Latitud"].iloc[0],
|
|
185
|
+
central,
|
|
186
|
+
f"-{inferior_limit}",
|
|
187
|
+
f"+{superior_limit}",
|
|
188
|
+
f"{{{int(porcentaje_cambio):,}}}",
|
|
189
|
+
p_value_mayor,
|
|
190
|
+
p_value_menor,
|
|
191
|
+
monitored_seasons,
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def set_axis_plot_config(ax):
|
|
196
|
+
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
|
|
197
|
+
ax.spines["right"].set_visible(False)
|
|
198
|
+
ax.spines["top"].set_visible(False)
|
|
199
|
+
plt.yticks(rotation=90, size=20)
|
|
200
|
+
locs_x, labels_x = plt.xticks()
|
|
201
|
+
locs_y, labels_y = plt.yticks()
|
|
202
|
+
plt.xticks(locs_x[1:], size=20)
|
|
203
|
+
plt.xlim(locs_x[0], locs_x[-1])
|
|
204
|
+
plt.ylim(0, locs_y[-1])
|
|
205
|
+
plt.xlabel("Año", size=20, labelpad=10)
|
|
206
|
+
plt.ylabel("Cantidad de parejas reproductoras", size=20)
|
|
@@ -37,15 +37,15 @@ def plot_population_trend(
|
|
|
37
37
|
lambda_latex = intervals_json["lambda_latex_interval"]
|
|
38
38
|
|
|
39
39
|
Modelo_Tendencia_Poblacional = Population_Trend_Model(fit_data, intervals, variable_of_interest)
|
|
40
|
-
Graficador = Plotter_Population_Trend_Model()
|
|
40
|
+
Graficador = Plotter_Population_Trend_Model(fit_data)
|
|
41
41
|
Graficador.plot_smooth(Modelo_Tendencia_Poblacional)
|
|
42
42
|
Graficador.plot_model(Modelo_Tendencia_Poblacional)
|
|
43
|
-
Graficador.plot_data(
|
|
43
|
+
Graficador.plot_data(variable_of_interest)
|
|
44
44
|
legend_mpl_object = Graficador.set_legend_location(island)
|
|
45
|
-
Graficador.set_x_lim(
|
|
45
|
+
Graficador.set_x_lim()
|
|
46
46
|
Graficador.set_y_lim(fit_data[variable_of_interest])
|
|
47
47
|
Graficador.set_labels()
|
|
48
|
-
Graficador.set_ticks(
|
|
48
|
+
Graficador.set_ticks()
|
|
49
49
|
Graficador.draw()
|
|
50
50
|
Graficador.plot_growth_rate_interval(legend_mpl_object, lambda_latex)
|
|
51
51
|
Graficador.savefig(island, output_path)
|
{population_trend-2.0.2 → population_trend-3.1.0}/population_trend/population_growth_model.py
RENAMED
|
@@ -4,12 +4,17 @@ from bootstrapping_tools import power_law, lambda_calculator
|
|
|
4
4
|
import matplotlib.pyplot as plt
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def
|
|
7
|
+
def normalize_seasons(df):
|
|
8
8
|
first_season = int(df.Temporada.min())
|
|
9
9
|
last_season = int(df.Temporada.max())
|
|
10
10
|
return np.linspace(first_season, last_season, last_season - first_season + 1).astype(int)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def calculate_model_domain(data):
|
|
14
|
+
last_value = data.Temporada.max() - data.Temporada.min()
|
|
15
|
+
return np.linspace(0, last_value, 100)
|
|
16
|
+
|
|
17
|
+
|
|
13
18
|
def calculate_upper_limit(data_interest_variable):
|
|
14
19
|
upper_limit = roundup(
|
|
15
20
|
data_interest_variable.max() * 1.2,
|
|
@@ -21,36 +26,36 @@ def calculate_upper_limit(data_interest_variable):
|
|
|
21
26
|
class Population_Trend_Model:
|
|
22
27
|
def __init__(self, fit_data, intervals, interest_variable):
|
|
23
28
|
self.intervals = intervals
|
|
24
|
-
self.
|
|
25
|
-
self.ticks_text = resample_seasons(fit_data)
|
|
26
|
-
self.ticks_positions = ticks_positions_array(self.ticks_text)
|
|
27
|
-
self.time_to_model = np.linspace(
|
|
28
|
-
self.ticks_positions.min(), self.ticks_positions.max(), 100
|
|
29
|
-
)
|
|
29
|
+
self.model_domain = calculate_model_domain(fit_data)
|
|
30
30
|
self.initial_population = lambda_calculator(
|
|
31
31
|
fit_data["Temporada"], fit_data[interest_variable]
|
|
32
32
|
)[1]
|
|
33
33
|
|
|
34
34
|
@property
|
|
35
35
|
def model_min(self):
|
|
36
|
-
return power_law(self.
|
|
36
|
+
return power_law(self.model_domain, self.intervals[0], self.initial_population)
|
|
37
37
|
|
|
38
38
|
@property
|
|
39
39
|
def model_med(self):
|
|
40
|
-
return power_law(self.
|
|
40
|
+
return power_law(self.model_domain, self.intervals[1], self.initial_population)
|
|
41
41
|
|
|
42
42
|
@property
|
|
43
43
|
def model_max(self):
|
|
44
|
-
return power_law(self.
|
|
44
|
+
return power_law(self.model_domain, self.intervals[2], self.initial_population)
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
class Plotter_Population_Trend_Model:
|
|
48
|
-
def __init__(self):
|
|
48
|
+
def __init__(self, data):
|
|
49
49
|
self.fig, self.ax = geci_plot()
|
|
50
|
+
self.data = data
|
|
51
|
+
self.plot_seasons = self.data["Temporada"][:] - self.data["Temporada"].iloc[0] + 1
|
|
52
|
+
self.ticks_text = normalize_seasons(self.data)
|
|
53
|
+
self.ticks_positions = ticks_positions_array(self.ticks_text)
|
|
54
|
+
self.plot_domain = np.linspace(self.ticks_positions.min(), self.ticks_positions.max(), 100)
|
|
50
55
|
|
|
51
56
|
def plot_smooth(self, Population_Trend_Model):
|
|
52
57
|
self.ax.fill_between(
|
|
53
|
-
|
|
58
|
+
self.plot_domain,
|
|
54
59
|
Population_Trend_Model.model_min,
|
|
55
60
|
Population_Trend_Model.model_max,
|
|
56
61
|
alpha=0.2,
|
|
@@ -60,17 +65,17 @@ class Plotter_Population_Trend_Model:
|
|
|
60
65
|
|
|
61
66
|
def plot_model(self, Population_Trend_Model):
|
|
62
67
|
plt.plot(
|
|
63
|
-
|
|
68
|
+
self.plot_domain,
|
|
64
69
|
Population_Trend_Model.model_med,
|
|
65
70
|
label="Population growth model",
|
|
66
71
|
color="b",
|
|
67
72
|
)
|
|
68
73
|
return self.fig
|
|
69
74
|
|
|
70
|
-
def plot_data(self,
|
|
75
|
+
def plot_data(self, variable_to_bootstrap):
|
|
71
76
|
plt.plot(
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
self.plot_seasons,
|
|
78
|
+
self.data[variable_to_bootstrap],
|
|
74
79
|
"-Dk",
|
|
75
80
|
label="Active Nests",
|
|
76
81
|
)
|
|
@@ -92,20 +97,20 @@ class Plotter_Population_Trend_Model:
|
|
|
92
97
|
calculate_upper_limit(fit_data),
|
|
93
98
|
)
|
|
94
99
|
|
|
95
|
-
def set_x_lim(self
|
|
100
|
+
def set_x_lim(self):
|
|
96
101
|
plt.xlim(
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
self.ticks_positions.min() - 0.2,
|
|
103
|
+
self.ticks_positions.max(),
|
|
99
104
|
)
|
|
100
105
|
|
|
101
106
|
def set_labels(self):
|
|
102
107
|
plt.ylabel("Number of breeding pairs", size=20)
|
|
103
108
|
plt.xlabel("Seasons", size=20)
|
|
104
109
|
|
|
105
|
-
def set_ticks(self
|
|
110
|
+
def set_ticks(self):
|
|
106
111
|
plt.xticks(
|
|
107
|
-
|
|
108
|
-
|
|
112
|
+
self.ticks_positions,
|
|
113
|
+
self.ticks_text,
|
|
109
114
|
rotation=90,
|
|
110
115
|
size=20,
|
|
111
116
|
)
|
|
File without changes
|
|
File without changes
|