@@ -106,9 +106,9 @@ def transition(
106106 """
107107 if not w_options :
108108 w_options = {}
109- assert (
110- unit_index in gdf . columns
111- ), f"The unit_index ( { unit_index } ) column is not in the geodataframe"
109+ assert unit_index in gdf . columns , (
110+ f"The unit_index ( { unit_index } ) column is not in the geodataframe"
111+ )
112112 gdf_temp = gdf .copy ().reset_index ()
113113 df = gdf_temp [[unit_index , temporal_index , cluster_col ]]
114114 df_wide = df .pivot (
@@ -208,9 +208,9 @@ def sequence(
208208 [5., 3., 2., 0., 0.]])
209209
210210 """
211- assert (
212- unit_index in gdf . columns
213- ), f"The unit_index ( { unit_index } ) column is not in the geodataframe"
211+ assert unit_index in gdf . columns , (
212+ f"The unit_index ( { unit_index } ) column is not in the geodataframe"
213+ )
214214 gdf_temp = gdf .copy ().reset_index ()
215215 df = gdf_temp [[unit_index , temporal_index , cluster_col ]]
216216 df_wide = (
@@ -287,16 +287,16 @@ def predict_markov_labels(
287287 if not w_options :
288288 w_options = {}
289289
290- assert (
291- cluster_col and cluster_col in gdf . columns
292- ), f"The input dataframe has no column named { cluster_col } "
290+ assert cluster_col and cluster_col in gdf . columns , (
291+ f"The input dataframe has no column named { cluster_col } "
292+ )
293293
294- assert (
295- base_year
296- ), "Missing `base_year`. You must provide an initial time point with labels to begin simulation"
297- assert (
298- base_year in gdf [ temporal_index ]. unique (). tolist ()
299- ), "A set of observations with `temporal_index`==`base_year` must be included in the gdf"
294+ assert base_year , (
295+ "Missing ` base_year`. You must provide an initial time point with labels to begin simulation"
296+ )
297+ assert base_year in gdf [ temporal_index ]. unique (). tolist (), (
298+ "A set of observations with `temporal_index`==` base_year` must be included in the gdf"
299+ )
300300
301301 gdf = gdf .copy ()
302302 gdf = gdf .dropna (subset = [cluster_col ]).reset_index (drop = True )
@@ -310,7 +310,6 @@ def predict_markov_labels(
310310 )
311311
312312 if time_steps == 1 :
313-
314313 gdf = gdf [gdf [temporal_index ] == base_year ].reset_index (drop = True )
315314 w = Ws [w_type ].from_dataframe (gdf , ** w_options )
316315 predicted = _draw_labels (w , gdf , cluster_col , t , unit_index , verbose )
@@ -319,9 +318,9 @@ def predict_markov_labels(
319318 return predicted
320319
321320 else :
322- assert (
323- increment
324- ), "You must set the `increment` argument to simulate multiple time steps"
321+ assert increment , (
322+ "You must set the ` increment` argument to simulate multiple time steps"
323+ )
325324 predictions = []
326325 gdf = gdf [gdf [temporal_index ] == base_year ]
327326 gdf = gdf [[unit_index , cluster_col , temporal_index , gdf .geometry .name ]]
@@ -368,8 +367,8 @@ def _draw_labels(w, gdf, cluster_col, markov, unit_index, verbose):
368367 gdf = gdf .copy ()
369368 gdf = gdf .dropna (subset = [cluster_col ])
370369 lags = lag_categorical (w , gdf [cluster_col ].values )
371- clusters = gdf .reset_index ()[cluster_col ].astype ( str ). values
372- classes = markov .classes . astype ( str )
370+ clusters = gdf .reset_index ()[cluster_col ].values
371+ classes = markov .classes
373372 cluster_idx = dict (zip (classes , list (range (len (classes )))))
374373
375374 labels = {}
@@ -380,9 +379,7 @@ def _draw_labels(w, gdf, cluster_col, markov, unit_index, verbose):
380379 ]
381380 # select the class row from the transition matrix using the unit's label
382381 probs = spatial_context [cluster_idx [clusters [i ]]]
383- probs /= (
384- probs .sum ()
385- ) # correct for tolerance, see https://stackoverflow.com/questions/25985120/numpy-1-9-0-valueerror-probabilities-do-not-sum-to-1
382+ probs /= probs .sum () # correct for tolerance, see https://stackoverflow.com/questions/25985120/numpy-1-9-0-valueerror-probabilities-do-not-sum-to-1
386383 probs = np .nan_to_num (probs .flatten ())
387384 if sum (probs ) == 0 :
388385 # in case obs have a modal neighbor never before seen in the model
@@ -391,7 +388,8 @@ def _draw_labels(w, gdf, cluster_col, markov, unit_index, verbose):
391388 if verbose :
392389 warn (
393390 f"Falling back to aspatial transition rule for unit "
394- f"{ gdf [unit_index ][i ]} "
391+ f"{ gdf [unit_index ][i ]} " ,
392+ stacklevel = 2 ,
395393 )
396394 probs = markov .p [cluster_idx [clusters [i ]]].flatten ()
397395
@@ -413,6 +411,7 @@ def draw_sequence_from_gdf(
413411 time_steps = 1 ,
414412 increment = None ,
415413 seed = None ,
414+ aspatial = False ,
416415):
417416 """Draw a set of class labels for each unit in a geodataframe using transition
418417 probabilities defined by a giddy.Spatial_Markov model and the spatial lag of each
@@ -463,7 +462,7 @@ def draw_sequence_from_gdf(
463462
464463 dfs = list ()
465464
466- current_df = gdf [gdf [time_column ] == start_time ]
465+ current_df = gdf [gdf [time_column ] == start_time ]. reset_index ()
467466
468467 if seed is None :
469468 seed = int (time ())
@@ -478,9 +477,9 @@ def draw_sequence_from_gdf(
478477 # `steps` and `dfs` are off by one so the ith in dfs is the previous time period
479478 current_df = dfs [i ].copy ()
480479 predicted_labels = _draw_labels_from_gdf (
481- current_df , w , label_column , smk , generators [i ]
480+ current_df , w , label_column , smk , generators [i ], aspatial = aspatial
482481 )
483- predicted_df = gdf [gdf [time_column ] == start_time ]
482+ predicted_df = gdf [gdf [time_column ] == start_time ]. copy (). reset_index ()
484483 # overwrite labels with predictions for the new time period
485484 predicted_df [label_column ] = predicted_labels
486485 predicted_df [time_column ] = step
@@ -491,7 +490,7 @@ def draw_sequence_from_gdf(
491490 return simulated
492491
493492
494- def _draw_labels_from_gdf (gdf , w , label_column , smk , seed ):
493+ def _draw_labels_from_gdf (gdf , w , label_column , smk , seed , aspatial = False ):
495494 """Draw set of new labels given a geodataframe and a spatial Markov transition model
496495
497496 Parameters
@@ -515,16 +514,16 @@ def _draw_labels_from_gdf(gdf, w, label_column, smk, seed):
515514 an array of simulated class labels drawn from the conditional probabilities
516515 provided in the Spatial_Markov object
517516 """
518-
519- labels = gdf [label_column ].astype ( str ). values
517+ classes = smk . classes
518+ labels = gdf [label_column ].values
520519 lags = lag_categorical (w , labels )
521- probs = _conditional_probs_from_smk (labels , lags , smk )
522- assert len (lags ) == len (
523- probs
524- ), "Lag values and probability vectors are different lengths"
525- simulated_labels = _draw_labels_from_probs (
526- smk .classes .astype (str ), probs = probs , seed = seed
520+ probs = _conditional_probs_from_smk (
521+ labels , lags , smk , fill_null_probs = True , aspatial = aspatial
527522 )
523+ assert len (lags ) == len (probs ), (
524+ "Lag values and probability vectors are different lengths"
525+ )
526+ simulated_labels = _draw_labels_from_probs (classes , probs = probs , seed = seed )
528527
529528 return simulated_labels
530529
@@ -549,21 +548,19 @@ def _draw_labels_from_probs(classes, probs, seed):
549548 list
550549 list of labels drawn from `classes` with size n_probs
551550 """
552- labels = list ()
553551 rng = default_rng (seed = seed )
552+ probs = pd .Series (probs )
554553 # for each set of probabilities in the array, draw a label
555554 # this could also be done in parallel. We could also take multiple draws per
556555 # unit by passing a `size` argument to rng.choice
557- for p in probs :
558- try :
559- label = rng .choice (classes , p = p )
560- except ValueError as e :
561- raise ValueError from e
562- labels .append (label )
563- return np .array (labels )
556+ probs = probs .apply (lambda x : rng .choice (classes , p = x ))
557+
558+ return probs .values
564559
565560
566- def _conditional_probs_from_smk (labels , lags , smk , fill_null_probs = True ):
561+ def _conditional_probs_from_smk (
562+ labels , lags , smk , fill_null_probs = True , aspatial = False
563+ ):
567564 """Given a set of existing labels and associated lags, return a vector of
568565 transition probabilities from a giddy.Spatial_Markov model
569566
@@ -583,38 +580,47 @@ def _conditional_probs_from_smk(labels, lags, smk, fill_null_probs=True):
583580 a set of transition probabilities, and each element of the innerr
584581 list(s) is the probability of transitioning into class with index i
585582 """
586- classes = smk .classes .astype (str )
583+ # classes = pd.Categorical(smk.classes)
584+ class_idx = dict (zip (smk .classes , range (len (smk .classes ))))
587585
588586 # mapping back to the order each class is given in the smk object
589- class_idx = dict (zip (classes , list (range (len (classes )))))
587+ # class_idx = dict(zip(classes, list(range(len(classes)))))
588+
589+ # print(class_idx)
590590
591591 probs = list ()
592592 # this piece could be parallelized as long as it gets concatenated back in order
593- for i in range ( len ( labels ) ):
593+ for i , _ in enumerate ( labels ):
594594 current_label = labels [i ]
595595 current_class_idx = class_idx [current_label ]
596+ # print(current_class_idx)
597+
596598 aspatial_p = np .nan_to_num (smk .p [current_class_idx ]).flatten ()
597599 aspatial_p /= aspatial_p .sum ()
598-
600+ # print(i,class_idx[lags[i]])
599601 lag_idx = class_idx [lags [i ]]
600602 conditional_matrix = smk .P [lag_idx ]
601603 p = conditional_matrix [current_class_idx ].flatten ()
602604 # correct for tolerance, see https://stackoverflow.com/questions/25985120/numpy-1-9-0-valueerror-probabilities-do-not-sum-to-1
603- p = np .nan_to_num (p )
604- if p .sum () == 0 :
605- if fill_null_probs :
606- warn (
607- f"No spatial transition rules for { current_label } with conrtext { lags [i ]} "
608- "falling back to aspatial transition rules"
609- )
610- probs .append (aspatial_p )
611- else :
612- raise ValueError (
613- f"No spatial transition rules for { current_label } with conrtext { lags [i ]} "
614- )
605+ # p = np.nan_to_num(p)
606+ if aspatial :
607+ probs .append (aspatial_p )
615608 else :
616- p /= p .sum ()
609+ if p .sum () == 0 :
610+ if fill_null_probs :
611+ warn (
612+ f"No spatial transition rules for { current_label } with context { lags [i ]} "
613+ "falling back to aspatial transition rules" ,
614+ stacklevel = 2 ,
615+ )
616+ probs .append (aspatial_p )
617+ else :
618+ raise ValueError (
619+ f"No spatial transition rules for { current_label } with context { lags [i ]} "
620+ )
621+ else :
622+ # p /= p.sum()
617623
618- probs .append (np .nan_to_num (p ))
624+ probs .append (np .nan_to_num (p ))
619625
620626 return probs
0 commit comments