huff 1.5.3__py3-none-any.whl → 1.5.5__py3-none-any.whl

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.
huff/models.py CHANGED
@@ -4,8 +4,8 @@
4
4
  # Author: Thomas Wieland
5
5
  # ORCID: 0000-0001-5168-9846
6
6
  # mail: geowieland@googlemail.com
7
- # Version: 1.5.3
8
- # Last update: 2025-07-15 17:22
7
+ # Version: 1.5.5
8
+ # Last update: 2025-07-26 13:42
9
9
  # Copyright (c) 2025 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -766,7 +766,10 @@ class InteractionMatrix:
766
766
  self.supply_locations.metadata = supply_locations_metadata
767
767
  self.customer_origins.metadata = customer_origins_metadata
768
768
 
769
- def utility(self):
769
+ def utility(
770
+ self,
771
+ check_df_vars: bool = True
772
+ ):
770
773
 
771
774
  interaction_matrix_df = self.interaction_matrix_df
772
775
 
@@ -781,10 +784,11 @@ class InteractionMatrix:
781
784
  if interaction_matrix_df["A_j"].isna().all():
782
785
  raise ValueError ("Error in utility calculation: Attraction variable is not defined")
783
786
 
784
- check_vars(
785
- df = interaction_matrix_df,
786
- cols = ["A_j", "t_ij"]
787
- )
787
+ if check_df_vars:
788
+ check_vars(
789
+ df = interaction_matrix_df,
790
+ cols = ["A_j", "t_ij"]
791
+ )
788
792
 
789
793
  customer_origins = self.customer_origins
790
794
  customer_origins_metadata = customer_origins.get_metadata()
@@ -793,9 +797,9 @@ class InteractionMatrix:
793
797
  if tc_weighting["func"] == "power":
794
798
  interaction_matrix_df["t_ij_weighted"] = interaction_matrix_df["t_ij"] ** tc_weighting["param"]
795
799
  elif tc_weighting["func"] == "exponential":
796
- interaction_matrix_df["t_ij_weighted"] = np.exp(tc_weighting["param"] * interaction_matrix_df['t_ij'])
800
+ interaction_matrix_df["t_ij_weighted"] = np.exp(tc_weighting["param"] * interaction_matrix_df["t_ij"])
797
801
  elif tc_weighting["func"] == "logistic":
798
- interaction_matrix_df["t_ij_weighted"] = 1+np.exp(tc_weighting["param"][0] + tc_weighting["param"][1] * interaction_matrix_df['t_ij'])
802
+ interaction_matrix_df["t_ij_weighted"] = 1+np.exp(tc_weighting["param"][0] + tc_weighting["param"][1] * interaction_matrix_df["t_ij"])
799
803
  else:
800
804
  raise ValueError ("Error in utility calculation: Transport costs weighting is not defined.")
801
805
 
@@ -818,8 +822,8 @@ class InteractionMatrix:
818
822
 
819
823
  for key, attrac_var in enumerate(attrac_vars):
820
824
 
821
- attrac_var_key = key #+1
822
- if attrac_var_key == 0: #1:
825
+ attrac_var_key = key
826
+ if attrac_var_key == 0:
823
827
  continue
824
828
 
825
829
  name = supply_locations_metadata["weighting"][attrac_var_key]["name"]
@@ -850,7 +854,10 @@ class InteractionMatrix:
850
854
 
851
855
  return self
852
856
 
853
- def probabilities (self):
857
+ def probabilities (
858
+ self,
859
+ check_df_vars: bool = True
860
+ ):
854
861
 
855
862
  interaction_matrix_df = self.interaction_matrix_df
856
863
 
@@ -858,6 +865,12 @@ class InteractionMatrix:
858
865
  self.utility()
859
866
  interaction_matrix_df = self.interaction_matrix_df
860
867
 
868
+ if check_df_vars:
869
+ check_vars(
870
+ df = interaction_matrix_df,
871
+ cols = ["U_ij"]
872
+ )
873
+
861
874
  utility_i = pd.DataFrame(interaction_matrix_df.groupby("i")["U_ij"].sum())
862
875
  utility_i = utility_i.rename(columns = {"U_ij": "U_i"})
863
876
 
@@ -876,7 +889,10 @@ class InteractionMatrix:
876
889
 
877
890
  return self
878
891
 
879
- def flows (self):
892
+ def flows (
893
+ self,
894
+ check_df_vars: bool = True
895
+ ):
880
896
 
881
897
  interaction_matrix_df = self.interaction_matrix_df
882
898
 
@@ -885,10 +901,11 @@ class InteractionMatrix:
885
901
  if interaction_matrix_df["C_i"].isna().all():
886
902
  raise ValueError ("Error in flows calculation: Market size column in customer origins not defined. Use CustomerOrigins.define_marketsize()")
887
903
 
888
- check_vars(
889
- df = interaction_matrix_df,
890
- cols = ["C_i"]
891
- )
904
+ if check_df_vars:
905
+ check_vars(
906
+ df = interaction_matrix_df,
907
+ cols = ["C_i"]
908
+ )
892
909
 
893
910
  if interaction_matrix_df["p_ij"].isna().all():
894
911
  self.probabilities()
@@ -900,15 +917,19 @@ class InteractionMatrix:
900
917
 
901
918
  return self
902
919
 
903
- def marketareas(self):
920
+ def marketareas(
921
+ self,
922
+ check_df_vars: bool = True
923
+ ):
904
924
 
905
925
  interaction_matrix_df = self.interaction_matrix_df
906
926
 
907
- check_vars(
908
- df = interaction_matrix_df,
909
- cols = ["E_ij"],
910
- check_zero = False
911
- )
927
+ if check_df_vars:
928
+ check_vars(
929
+ df = interaction_matrix_df,
930
+ cols = ["E_ij"],
931
+ check_zero = False
932
+ )
912
933
 
913
934
  market_areas_df = pd.DataFrame(interaction_matrix_df.groupby("j")["E_ij"].sum())
914
935
  market_areas_df = market_areas_df.reset_index(drop=False)
@@ -923,15 +944,21 @@ class InteractionMatrix:
923
944
 
924
945
  def hansen(
925
946
  self,
926
- from_origins: bool = True
947
+ from_origins: bool = True,
948
+ exclude_self: bool = True,
949
+ check_df_vars: bool = True
927
950
  ):
928
951
 
929
952
  interaction_matrix_df = self.interaction_matrix_df
930
953
 
954
+ if exclude_self:
955
+
956
+ interaction_matrix_df = interaction_matrix_df[interaction_matrix_df["i"] != interaction_matrix_df["j"]]
957
+
931
958
  if from_origins:
932
959
 
933
- if interaction_matrix_df["U_ij"].isna().all():
934
- self.utility()
960
+ if "U_ij" not in interaction_matrix_df.columns or interaction_matrix_df["U_ij"].isna().all():
961
+ self.utility(check_df_vars = check_df_vars)
935
962
  interaction_matrix_df = self.interaction_matrix_df
936
963
 
937
964
  hansen_df = pd.DataFrame(interaction_matrix_df.groupby("i")["U_ij"].sum()).reset_index()
@@ -947,9 +974,9 @@ class InteractionMatrix:
947
974
  if tc_weighting["func"] == "power":
948
975
  interaction_matrix_df["t_ij_weighted"] = interaction_matrix_df["t_ij"] ** tc_weighting["param"]
949
976
  elif tc_weighting["func"] == "exponential":
950
- interaction_matrix_df["t_ij_weighted"] = np.exp(tc_weighting["param"] * interaction_matrix_df['t_ij'])
977
+ interaction_matrix_df["t_ij_weighted"] = np.exp(tc_weighting["param"] * interaction_matrix_df["t_ij"])
951
978
  elif tc_weighting["func"] == "logistic":
952
- interaction_matrix_df["t_ij_weighted"] = 1+np.exp(tc_weighting["param"][0] + tc_weighting["param"][1] * interaction_matrix_df['t_ij'])
979
+ interaction_matrix_df["t_ij_weighted"] = 1+np.exp(tc_weighting["param"][0] + tc_weighting["param"][1] * interaction_matrix_df["t_ij"])
953
980
  else:
954
981
  raise ValueError ("Error in hansen accessibility calculation: Transport costs weighting is not defined.")
955
982
 
@@ -1073,7 +1100,8 @@ class InteractionMatrix:
1073
1100
  def loglik(
1074
1101
  self,
1075
1102
  params,
1076
- fit_by = "probabilities"
1103
+ fit_by = "probabilities",
1104
+ check_df_vars: bool = True
1077
1105
  ):
1078
1106
 
1079
1107
  if fit_by not in ["probabilities", "flows"]:
@@ -1160,9 +1188,9 @@ class InteractionMatrix:
1160
1188
 
1161
1189
  interaction_matrix_copy = copy.deepcopy(self)
1162
1190
 
1163
- interaction_matrix_copy.utility()
1164
- interaction_matrix_copy.probabilities()
1165
- interaction_matrix_copy.flows()
1191
+ interaction_matrix_copy.utility(check_df_vars = check_df_vars)
1192
+ interaction_matrix_copy.probabilities(check_df_vars = check_df_vars)
1193
+ interaction_matrix_copy.flows(check_df_vars = check_df_vars)
1166
1194
 
1167
1195
  interaction_matrix_df_copy = interaction_matrix_copy.get_interaction_matrix_df()
1168
1196
 
@@ -1196,7 +1224,8 @@ class InteractionMatrix:
1196
1224
  bounds: list = [(0.5, 1), (-3, -1)],
1197
1225
  constraints: list = [],
1198
1226
  fit_by = "probabilities",
1199
- update_estimates: bool = True
1227
+ update_estimates: bool = True,
1228
+ check_df_vars: bool = True
1200
1229
  ):
1201
1230
 
1202
1231
  supply_locations = self.supply_locations
@@ -1232,7 +1261,7 @@ class InteractionMatrix:
1232
1261
  ml_result = minimize(
1233
1262
  self.loglik,
1234
1263
  initial_params,
1235
- args=fit_by,
1264
+ args=(fit_by, check_df_vars),
1236
1265
  method = method,
1237
1266
  bounds = bounds,
1238
1267
  constraints = constraints,
@@ -1745,7 +1774,8 @@ class HuffModel:
1745
1774
 
1746
1775
  def loglik(
1747
1776
  self,
1748
- params
1777
+ params,
1778
+ check_df_vars: bool = True
1749
1779
  ):
1750
1780
 
1751
1781
  if not isinstance(params, list):
@@ -1827,9 +1857,9 @@ class HuffModel:
1827
1857
 
1828
1858
  interaction_matrix_copy = copy.deepcopy(huff_model_copy.interaction_matrix)
1829
1859
 
1830
- interaction_matrix_copy = interaction_matrix_copy.utility()
1831
- interaction_matrix_copy = interaction_matrix_copy.probabilities()
1832
- interaction_matrix_copy = interaction_matrix_copy.flows()
1860
+ interaction_matrix_copy = interaction_matrix_copy.utility(check_df_vars = check_df_vars)
1861
+ interaction_matrix_copy = interaction_matrix_copy.probabilities(check_df_vars = check_df_vars)
1862
+ interaction_matrix_copy = interaction_matrix_copy.flows(check_df_vars = check_df_vars)
1833
1863
 
1834
1864
  huff_model_copy = interaction_matrix_copy.marketareas()
1835
1865
 
@@ -1855,7 +1885,8 @@ class HuffModel:
1855
1885
  constraints: list = [],
1856
1886
  fit_by = "probabilities",
1857
1887
  update_estimates: bool = True,
1858
- check_numbers: bool = True
1888
+ check_numbers: bool = True,
1889
+ check_df_vars: bool = True
1859
1890
  ):
1860
1891
 
1861
1892
  if fit_by in ["probabilities", "flows"]:
@@ -1866,7 +1897,8 @@ class HuffModel:
1866
1897
  bounds = bounds,
1867
1898
  constraints = constraints,
1868
1899
  fit_by = fit_by,
1869
- update_estimates = update_estimates
1900
+ update_estimates = update_estimates,
1901
+ check_df_vars = check_df_vars
1870
1902
  )
1871
1903
 
1872
1904
  elif fit_by == "totals":
@@ -1914,6 +1946,7 @@ class HuffModel:
1914
1946
  ml_result = minimize(
1915
1947
  self.loglik,
1916
1948
  initial_params,
1949
+ args=check_df_vars,
1917
1950
  method = method,
1918
1951
  bounds = bounds,
1919
1952
  constraints = constraints,
@@ -1999,11 +2032,11 @@ class HuffModel:
1999
2032
 
2000
2033
  else:
2001
2034
 
2002
- self.interaction_matrix.utility()
2003
- self.interaction_matrix.probabilities()
2004
- self.interaction_matrix.flows()
2035
+ self.interaction_matrix.utility(check_df_vars = check_df_vars)
2036
+ self.interaction_matrix.probabilities(check_df_vars = check_df_vars)
2037
+ self.interaction_matrix.flows(check_df_vars = check_df_vars)
2005
2038
 
2006
- huff_model_new_marketareas = self.interaction_matrix.marketareas()
2039
+ huff_model_new_marketareas = self.interaction_matrix.marketareas(check_df_vars = check_df_vars)
2007
2040
  self.market_areas_df["T_j"] = huff_model_new_marketareas.get_market_areas_df()["T_j"]
2008
2041
 
2009
2042
  self.interaction_matrix.metadata["fit"] = {
@@ -2372,7 +2405,8 @@ class MCIModel:
2372
2405
 
2373
2406
  def utility(
2374
2407
  self,
2375
- transformation = "LCT"
2408
+ transformation = "LCT",
2409
+ check_df_vars: bool = True
2376
2410
  ):
2377
2411
 
2378
2412
  interaction_matrix = self.interaction_matrix
@@ -2384,10 +2418,11 @@ class MCIModel:
2384
2418
  if interaction_matrix_df["A_j"].isna().all():
2385
2419
  raise ValueError ("Error in utility calculation: Attraction variable is not defined")
2386
2420
 
2387
- check_vars(
2388
- df = interaction_matrix_df,
2389
- cols = ["A_j", "t_ij"]
2390
- )
2421
+ if check_df_vars:
2422
+ check_vars(
2423
+ df = interaction_matrix_df,
2424
+ cols = ["A_j", "t_ij"]
2425
+ )
2391
2426
 
2392
2427
  customer_origins = interaction_matrix.get_customer_origins()
2393
2428
  customer_origins_metadata = customer_origins.get_metadata()
@@ -2476,7 +2511,8 @@ class MCIModel:
2476
2511
 
2477
2512
  def flows (
2478
2513
  self,
2479
- transformation = "LCT"
2514
+ transformation = "LCT",
2515
+ check_df_vars: bool = True
2480
2516
  ):
2481
2517
 
2482
2518
  interaction_matrix = self.interaction_matrix
@@ -2488,10 +2524,11 @@ class MCIModel:
2488
2524
  if interaction_matrix_df["C_i"].isna().all():
2489
2525
  raise ValueError ("Error in flows calculation: Market size column in customer origins not defined. Use CustomerOrigins.define_marketsize()")
2490
2526
 
2491
- check_vars(
2492
- df = interaction_matrix_df,
2493
- cols = ["C_i"]
2494
- )
2527
+ if check_df_vars:
2528
+ check_vars(
2529
+ df = interaction_matrix_df,
2530
+ cols = ["C_i"]
2531
+ )
2495
2532
 
2496
2533
  if interaction_matrix_df["p_ij"].isna().all():
2497
2534
  self.probabilities(transformation=transformation)
@@ -2504,15 +2541,19 @@ class MCIModel:
2504
2541
 
2505
2542
  return self
2506
2543
 
2507
- def marketareas (self):
2544
+ def marketareas (
2545
+ self,
2546
+ check_df_vars: bool = True
2547
+ ):
2508
2548
 
2509
2549
  interaction_matrix = self.interaction_matrix
2510
2550
  interaction_matrix_df = interaction_matrix.get_interaction_matrix_df()
2511
2551
 
2512
- check_vars(
2513
- df = interaction_matrix_df,
2514
- cols = ["E_ij"]
2515
- )
2552
+ if check_df_vars:
2553
+ check_vars(
2554
+ df = interaction_matrix_df,
2555
+ cols = ["E_ij"]
2556
+ )
2516
2557
 
2517
2558
  market_areas_df = pd.DataFrame(interaction_matrix_df.groupby("j")["E_ij"].sum())
2518
2559
  market_areas_df = market_areas_df.reset_index(drop=False)
@@ -2735,7 +2776,7 @@ def load_interaction_matrix(
2735
2776
  xlsx_sheet: str = None,
2736
2777
  crs_input = "EPSG:4326",
2737
2778
  crs_output = "EPSG:4326",
2738
- check_df_vars = True
2779
+ check_df_vars: bool = True
2739
2780
  ):
2740
2781
 
2741
2782
  if isinstance(data, pd.DataFrame):
@@ -2976,7 +3017,7 @@ def load_marketareas(
2976
3017
  csv_decimal = ",",
2977
3018
  csv_encoding="unicode_escape",
2978
3019
  xlsx_sheet: str = None,
2979
- check_df_vars = True
3020
+ check_df_vars: bool = True
2980
3021
  ):
2981
3022
 
2982
3023
  if isinstance(data, pd.DataFrame):
huff/tests/tests_huff.py CHANGED
@@ -4,8 +4,8 @@
4
4
  # Author: Thomas Wieland
5
5
  # ORCID: 0000-0001-5168-9846
6
6
  # mail: geowieland@googlemail.com
7
- # Version: 1.5.3
8
- # Last update: 2025-07-15 17:22
7
+ # Version: 1.5.4
8
+ # Last update: 2025-07-18 18:06
9
9
  # Copyright (c) 2025 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: huff
3
- Version: 1.5.3
3
+ Version: 1.5.5
4
4
  Summary: huff: Huff Model Market Area Analysis
5
5
  Author: Thomas Wieland
6
6
  Author-email: geowieland@googlemail.com
@@ -28,12 +28,10 @@ Thomas Wieland [ORCID](https://orcid.org/0000-0001-5168-9846) [EMail](mailto:geo
28
28
  See the /tests directory for usage examples of most of the included functions.
29
29
 
30
30
 
31
- ## Updates v1.5.3
31
+ ## Updates v1.5.5
32
32
  - Bugfixes:
33
- - InteractionMatrix.summary() and HuffModel.summary(): No KeyError when param is None anymore
34
- - Extensions:
35
- - Confidence intervals (bootstrap) for Huff Model ML estimations
36
- - ValueError and KeyError are more precise (function is included)
33
+ - Removing i = j cases in InteractionMatrix.hansen()
34
+ - InteractionMatrix.utility(): Check for utility column
37
35
 
38
36
 
39
37
  ## Features
@@ -1,10 +1,10 @@
1
1
  huff/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  huff/gistools.py,sha256=fgeE1IsUO7UIaawb23kuiz_Rlxn7T18iLLTA5yvgp74,7038
3
- huff/models.py,sha256=mPASlL0YA8x-cnhoRgrpr1sP-p5gGg1_cwM-QGf8GfU,133310
3
+ huff/models.py,sha256=yrV4enajAG0bHCgrGh3w-SNX5kJDJnuE28i6RXN30HE,134903
4
4
  huff/ors.py,sha256=JlO2UEishQX87PIiktksOrVT5QdB-GEWgjXcxoR_KuA,11929
5
5
  huff/osm.py,sha256=9A-7hxeZyjA2r8w2_IqqwH14qq2Y9AS1GxVKOD7utqs,7747
6
6
  huff/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- huff/tests/tests_huff.py,sha256=FMnkSs7id4KcJei71DRLNPbY7cvOVjALwYzcBKCm1Ao,13116
7
+ huff/tests/tests_huff.py,sha256=np0YvzmGYqVFCPFBsdFuUWtGlwX6HKUUlWkYlfWzGPY,13116
8
8
  huff/tests/data/Haslach.cpg,sha256=OtMDH1UDpEBK-CUmLugjLMBNTqZoPULF3QovKiesmCQ,5
9
9
  huff/tests/data/Haslach.dbf,sha256=GVPIt05OzDO7UrRDcsMhiYWvyXAPg6Z-qkiysFzj-fc,506
10
10
  huff/tests/data/Haslach.prj,sha256=2Jy1Vlzh7UxQ1MXpZ9UYLs2SxfrObj2xkEkZyLqmGTY,437
@@ -24,7 +24,7 @@ huff/tests/data/Haslach_supermarkets.qmd,sha256=JlcOYzG4vI1NH1IuOpxwIPnJsCyC-pDR
24
24
  huff/tests/data/Haslach_supermarkets.shp,sha256=X7QbQ0BTMag_B-bDRbpr-go2BQIXo3Y8zMAKpYZmlps,324
25
25
  huff/tests/data/Haslach_supermarkets.shx,sha256=j23QHX-SmdAeN04rw0x8nUOran-OCg_T6r_LvzzEPWs,164
26
26
  huff/tests/data/Wieland2015.xlsx,sha256=H4rxCFlctn44-O6mIyeFf67FlgvznLX7xZqpoWYS41A,25788
27
- huff-1.5.3.dist-info/METADATA,sha256=vqlH9tlDz5pHu85fYu1Bnprk9yiWzAPRY94rQTQxFGM,6142
28
- huff-1.5.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
29
- huff-1.5.3.dist-info/top_level.txt,sha256=nlzX-PxZNFmIxANIJMySuIFPihd6qOBkRlhIC28NEsQ,5
30
- huff-1.5.3.dist-info/RECORD,,
27
+ huff-1.5.5.dist-info/METADATA,sha256=EmguUFYBD-ZCMDzsciDWpbsgJJb5jdJolrHxSh1KmfA,6009
28
+ huff-1.5.5.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
29
+ huff-1.5.5.dist-info/top_level.txt,sha256=nlzX-PxZNFmIxANIJMySuIFPihd6qOBkRlhIC28NEsQ,5
30
+ huff-1.5.5.dist-info/RECORD,,
File without changes