howso-visuals 2.5.6__py3-none-any.whl → 3.0.0__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.
@@ -53,9 +53,9 @@ def test_plot_interpretable_prediction_react(
53
53
  desired_conviction=5_000,
54
54
  num_cases_to_generate=20,
55
55
  )
56
- generative_reacts = result["action"].loc[:, action_feature].values.tolist()
57
- generative_reacts[0] = generative_reacts[0] + 0.2
58
- generative_reacts[-1] = generative_reacts[-1] - 0.2
56
+ generative_reacts = result["action"]
57
+ generative_reacts.loc[0, action_feature] = generative_reacts.loc[0, action_feature] + 0.2
58
+ generative_reacts.loc[19, action_feature] = generative_reacts.loc[19, action_feature] - 0.2
59
59
  else:
60
60
  generative_reacts = None
61
61
 
howso/visuals/visuals.py CHANGED
@@ -479,10 +479,11 @@ def plot_interpretable_prediction(
479
479
  react: "Reaction",
480
480
  *,
481
481
  actual_value: float | None = None,
482
- generative_reacts: list[float] | None = None,
482
+ generative_reacts: pd.DataFrame | None = None,
483
483
  residual: float | None = None,
484
484
  secondary_yaxis_title: str = "Influence Weight",
485
- title: str | None = None,
485
+ subplot_titles: list[str] | None = None,
486
+ title: str = "Interpretable Predictions",
486
487
  tooltip_features: Collection[str] | None = None,
487
488
  xaxis_title: str | None = None,
488
489
  yaxis_title: str = "Density",
@@ -495,10 +496,10 @@ def plot_interpretable_prediction(
495
496
  react : Reaction
496
497
  The reaction predicting the action feature(s) to visualize. If this contains more than one action feature,
497
498
  each will be given its own plot.
498
- generative_reacts : list of float, optional
499
- An optional list of values for the action feature to visualize. This will be used to visualize a
500
- KDE plot to characterize the distribution of values around the predicted and actual values. If this is None,
501
- the distribution of influential cases in the react will be used instead, if present.
499
+ generative_reacts : DataFrame, optional
500
+ Ann optional `DataFrame` of generative reactions which will be used to visualize a KDE around
501
+ the predicted value. If this is None, the distribution of influential cases in the react will
502
+ be used instead, if present.
502
503
  actual_value : float, optional
503
504
  The actual value for the point that was predicted. If this is None, only the predicted value will be
504
505
  visualized.
@@ -507,7 +508,9 @@ def plot_interpretable_prediction(
507
508
  predicted value. If this is None, no error bar will be displayed
508
509
  secondary_yaxis_title : str, default "Influence Weight"
509
510
  The title for the figure's secondary y axis.
510
- title : str, optional
511
+ subplot_titles : list[str], optional
512
+ A list of titles to use for subplots. If this is None, titles will be auto-generated.
513
+ title : str, default "Interpretable Prediction"
511
514
  The title for the figure.
512
515
  tooltip_features : Collection[str], optional
513
516
  Additional features to include in the tooltip when hovering over an influential case in the plot.
@@ -521,31 +524,52 @@ def plot_interpretable_prediction(
521
524
  Figure or list of Figure
522
525
  The resultant `Plotly` figure(s).
523
526
  """
524
- figures = []
525
527
  tooltip_features = list(tooltip_features) if tooltip_features is not None else []
528
+ action_features = react["action"].columns
529
+ num_rows = len(action_features)
530
+ subplot_titles = subplot_titles or [f"Interpretable Prediction for: {af}" for af in action_features]
531
+ case_hover_template = "<b>Value:</b> %{x}<br><b>Influence:</b> N/A"
532
+ fig = make_subplots(
533
+ rows=num_rows,
534
+ specs=[[{"secondary_y": True}]] * num_rows,
535
+ subplot_titles=subplot_titles,
536
+ )
526
537
 
527
- for action_feature in react["action"].columns:
538
+ for row, action_feature in enumerate(action_features):
528
539
  predicted_value = react["action"][action_feature].iloc[0]
540
+ legend = "legend" if row == 0 else f"legend{row + 1}"
529
541
 
530
542
  influential_cases = react.get("details", {}).get("influential_cases")
531
543
  influential_cases = influential_cases[0] if influential_cases else None
532
544
 
533
545
  if generative_reacts is not None:
534
- action_distribution = generative_reacts
535
- else:
546
+ action_distribution = generative_reacts[action_feature]
547
+ elif influential_cases is not None:
536
548
  action_distribution = [c[action_feature] for c in influential_cases]
537
- action_kde = gaussian_kde(action_distribution)
538
- density_x = np.linspace(
539
- min(action_distribution) * 0.6,
540
- max(action_distribution) * 1.4,
541
- len(action_distribution),
542
- )
543
- density_y = action_kde(density_x)
544
- fig = make_subplots(specs=[[{"secondary_y": True}]])
545
- fig.add_trace(
546
- go.Scattergl(x=density_x, y=density_y, fill="tonexty", name="Distribution", hoverinfo="skip", mode="lines")
547
- )
548
- case_hover_template = "<b>Value:</b> %{x}<br><b>Influence:</b> N/A"
549
+ else:
550
+ action_distribution = None
551
+
552
+ if action_distribution is not None:
553
+ action_kde = gaussian_kde(action_distribution)
554
+ density_x = np.linspace(
555
+ min(action_distribution) * 0.6,
556
+ max(action_distribution) * 1.4,
557
+ len(action_distribution),
558
+ )
559
+ density_y = action_kde(density_x)
560
+ fig.add_trace(
561
+ go.Scattergl(
562
+ x=density_x,
563
+ y=density_y,
564
+ fill="tonexty",
565
+ name="Distribution",
566
+ hoverinfo="skip",
567
+ mode="lines",
568
+ legend=legend,
569
+ ),
570
+ row=row + 1,
571
+ col=1,
572
+ )
549
573
  predicted_case_hover_template = case_hover_template
550
574
 
551
575
  if influential_cases is not None:
@@ -561,14 +585,17 @@ def plot_interpretable_prediction(
561
585
  fig.add_trace(
562
586
  go.Scattergl(
563
587
  x=[predicted_value],
564
- y=[max_inf_weight * 1.05],
588
+ y=[max_inf_weight * 1.05] if max_inf_weight else 1,
565
589
  name="Predicted Value",
566
590
  mode="markers",
567
591
  marker={"size": 15, "color": "purple"},
568
592
  hovertemplate=predicted_case_hover_template,
569
593
  error_x=error_x,
594
+ legend=legend,
570
595
  ),
571
596
  secondary_y=True,
597
+ row=row + 1,
598
+ col=1,
572
599
  )
573
600
 
574
601
  if actual_value is not None:
@@ -576,19 +603,22 @@ def plot_interpretable_prediction(
576
603
  fig.add_trace(
577
604
  go.Scattergl(
578
605
  x=[actual_value],
579
- y=[max_inf_weight * 1.05],
606
+ y=[max_inf_weight * 1.05] if max_inf_weight else 1,
580
607
  name="Actual Value",
581
608
  mode="markers",
582
609
  marker=dict(size=15, color="orange"),
583
610
  hovertemplate=case_hover_template,
611
+ legend=legend,
584
612
  ),
585
613
  secondary_y=True,
614
+ row=row + 1,
615
+ col=1,
586
616
  )
587
617
 
588
618
  # Update axes, hover mode
589
- fig.update_xaxes(title_text=xaxis_title or action_feature, autorange=True)
590
- fig.update_yaxes(title_text=yaxis_title, color="blue", secondary_y=False)
591
- fig.update_yaxes(title_text=secondary_yaxis_title, color="green", secondary_y=True)
619
+ fig.update_xaxes(title_text=xaxis_title or action_feature, autorange=True, row=row + 1, col=1)
620
+ fig.update_yaxes(title_text=yaxis_title, color="blue", secondary_y=False, row=row + 1, col=1)
621
+ fig.update_yaxes(title_text=secondary_yaxis_title, color="green", secondary_y=True, row=row + 1, col=1)
592
622
 
593
623
  if influential_cases is not None and len(influential_cases):
594
624
  inf_case_hover_template = (
@@ -621,19 +651,24 @@ def plot_interpretable_prediction(
621
651
  customdata=inf_case_labels,
622
652
  mode="markers",
623
653
  marker=dict(color="green"),
654
+ legend=legend,
624
655
  ),
625
656
  secondary_y=True,
657
+ row=row + 1,
658
+ col=1,
626
659
  )
627
660
 
628
- if title is None:
629
- title = f"Interpretable Prediction for: {action_feature}"
630
- fig.update_layout(title=dict(text=title))
631
-
632
- figures.append(fig)
661
+ # Increase height based on the number of rows
662
+ fig.update_layout(height=300 * num_rows)
663
+ # Place a legend to the right of each subplot.
664
+ for i, yaxis in enumerate(fig.select_yaxes(secondary_y=False), 1):
665
+ legend_name = f"legend{i}"
666
+ fig.update_layout({legend_name: dict(y=yaxis.domain[1], yanchor="top")}, showlegend=True)
667
+ fig.update_traces(row=i, legend=legend_name)
633
668
 
634
- if len(figures) == 1:
635
- return figures[0]
636
- return figures
669
+ title = title or "Interpretable Predictions"
670
+ fig.update_layout(title=title)
671
+ return fig
637
672
 
638
673
 
639
674
  def plot_fairness_disparity(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: howso-visuals
3
- Version: 2.5.6
3
+ Version: 3.0.0
4
4
  Summary: Visualization utilities for use with Howso Engine.
5
5
  License: GNU AFFERO GENERAL PUBLIC LICENSE
6
6
  Version 3, 19 November 2007
@@ -9,14 +9,14 @@ config/powershell/Helper-Functions.ps1,sha256=oLEunSYLr-zxtG9kqYuOGG8v3kSxTQEpqe
9
9
  howso/visuals/__init__.py,sha256=OL9LT4186YIXkEmM4qc21maRnfrtxBmFUHnalcKzGWA,507
10
10
  howso/visuals/colors.py,sha256=2ChjT4MUjA4ZqITR_K1j3fUKNRBd8M9iyJ4ZT10cNlw,1709
11
11
  howso/visuals/graph.py,sha256=fdZzYLwoBV8CeW_SwtcNkDh8xbIG_FlECSECxyBIr5M,8311
12
- howso/visuals/visuals.py,sha256=Aruqe2hUWCTcF2t_2HVbubvPF7RO7RG8RUxvr8EnMBg,33158
12
+ howso/visuals/visuals.py,sha256=0jaM1f7Tzh6JVwrtNxQuVyhcOmgYoE-uu_-zzWef09w,34649
13
13
  howso/visuals/data/iris.csv,sha256=73iTb6rB42LgZwf89er-tEbuyjlvEAx6WywMPwt0GjU,2757
14
14
  howso/visuals/tests/conftest.py,sha256=BcvCVDWbwaQKSstzJzN9G48KoDnCgTtVdgYo2CJZTWM,1293
15
15
  howso/visuals/tests/test_graph.py,sha256=gfuJNpsNBLx2tDXDIR-ClbCZUSOQCmk8Dx2X-NhN2yM,1014
16
- howso/visuals/tests/test_plot.py,sha256=lOYYUty84LFFdaQCDcw-fMAicTqlHIwvNtuVch0RIEE,8121
17
- howso_visuals-2.5.6.dist-info/licenses/LICENSE-3RD-PARTY.txt,sha256=aosupVocnnJ4n99ICajoFbXaRnZstEbtxTuVaT6RWUQ,373165
18
- howso_visuals-2.5.6.dist-info/licenses/LICENSE.txt,sha256=2xqHuoHohba7gpcZZKtOICRjzeKsQANXG8WoV9V35KM,33893
19
- howso_visuals-2.5.6.dist-info/METADATA,sha256=ogmqaaDN4dIIimuzPcEvRjPSF5gXFl6DQjtHlvzH3E8,41051
20
- howso_visuals-2.5.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
- howso_visuals-2.5.6.dist-info/top_level.txt,sha256=4ltSHx7mNsXczuoCPkz1irjq1x1JZir2QrX-ZwRNWXU,22
22
- howso_visuals-2.5.6.dist-info/RECORD,,
16
+ howso/visuals/tests/test_plot.py,sha256=iVdEyuIRYEN23IZx4He3hjDvBwNOP9NsXqdrQPSYHaw,8162
17
+ howso_visuals-3.0.0.dist-info/licenses/LICENSE-3RD-PARTY.txt,sha256=riZoS_SaicJQ2vten1_zvA3p0Y46ecEiJiDqZ6QoRCg,373165
18
+ howso_visuals-3.0.0.dist-info/licenses/LICENSE.txt,sha256=2xqHuoHohba7gpcZZKtOICRjzeKsQANXG8WoV9V35KM,33893
19
+ howso_visuals-3.0.0.dist-info/METADATA,sha256=0Y5RXJsuOvNyb0ISM8T0CuDYx-z7RHdlZOSWdh7bTTA,41051
20
+ howso_visuals-3.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
+ howso_visuals-3.0.0.dist-info/top_level.txt,sha256=4ltSHx7mNsXczuoCPkz1irjq1x1JZir2QrX-ZwRNWXU,22
22
+ howso_visuals-3.0.0.dist-info/RECORD,,
@@ -1,10 +1,10 @@
1
1
  Faker
2
- 38.0.0
2
+ 38.2.0
3
3
  MIT License
4
4
  joke2k
5
5
  https://github.com/joke2k/faker
6
6
  Faker is a Python package that generates fake data for you.
7
- /home/runner/.pyenv/versions/3.13.9/lib/python3.13/site-packages/faker-38.0.0.dist-info/licenses/LICENSE.txt
7
+ /home/runner/.pyenv/versions/3.13.9/lib/python3.13/site-packages/faker-38.2.0.dist-info/licenses/LICENSE.txt
8
8
  Copyright (c) 2012 Daniele Faraglia
9
9
 
10
10
  Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -297,12 +297,12 @@ OTHER DEALINGS IN THE SOFTWARE.
297
297
 
298
298
 
299
299
  click
300
- 8.3.0
300
+ 8.3.1
301
301
  BSD-3-Clause
302
302
  UNKNOWN
303
303
  https://github.com/pallets/click/
304
304
  Composable command line interface toolkit
305
- /home/runner/.pyenv/versions/3.13.9/lib/python3.13/site-packages/click-8.3.0.dist-info/licenses/LICENSE.txt
305
+ /home/runner/.pyenv/versions/3.13.9/lib/python3.13/site-packages/click-8.3.1.dist-info/licenses/LICENSE.txt
306
306
  Copyright 2014 Pallets
307
307
 
308
308
  Redistribution and use in source and binary forms, with or without
@@ -1232,12 +1232,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1232
1232
  SOFTWARE.
1233
1233
 
1234
1234
  narwhals
1235
- 2.11.0
1235
+ 2.12.0
1236
1236
  MIT License
1237
1237
  Marco Gorelli <hello_narwhals@proton.me>
1238
1238
  https://github.com/narwhals-dev/narwhals
1239
1239
  Extremely lightweight compatibility layer between dataframe libraries
1240
- /home/runner/.pyenv/versions/3.13.9/lib/python3.13/site-packages/narwhals-2.11.0.dist-info/licenses/LICENSE.md
1240
+ /home/runner/.pyenv/versions/3.13.9/lib/python3.13/site-packages/narwhals-2.12.0.dist-info/licenses/LICENSE.md
1241
1241
  MIT License
1242
1242
 
1243
1243
  Copyright (c) 2024, Marco Gorelli
@@ -5370,12 +5370,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5370
5370
 
5371
5371
 
5372
5372
  plotly
5373
- 6.4.0
5373
+ 6.5.0
5374
5374
  MIT License
5375
5375
  Chris P <chris@plot.ly>
5376
5376
  https://plotly.com/python/
5377
5377
  An open-source interactive data visualization library for Python
5378
- /home/runner/.pyenv/versions/3.13.9/lib/python3.13/site-packages/plotly-6.4.0.dist-info/licenses/LICENSE.txt
5378
+ /home/runner/.pyenv/versions/3.13.9/lib/python3.13/site-packages/plotly-6.5.0.dist-info/licenses/LICENSE.txt
5379
5379
  MIT License
5380
5380
 
5381
5381
  Copyright (c) 2016-2024 Plotly Technologies Inc.