fiddler-client 3.8.3__tar.gz → 3.9.1__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.
Files changed (121) hide show
  1. {fiddler-client-3.8.3/fiddler_client.egg-info → fiddler-client-3.9.1}/PKG-INFO +1 -1
  2. fiddler-client-3.9.1/fiddler/VERSION +1 -0
  3. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/webhook.py +2 -2
  4. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/webhook.py +3 -1
  5. fiddler-client-3.9.1/fiddler/tests/test_utils.py +431 -0
  6. fiddler-client-3.9.1/fiddler/utils/column_generator.py +68 -0
  7. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/utils/model_generator.py +1 -0
  8. {fiddler-client-3.8.3 → fiddler-client-3.9.1/fiddler_client.egg-info}/PKG-INFO +1 -1
  9. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler_client.egg-info/SOURCES.txt +1 -0
  10. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler_client.egg-info/requires.txt +2 -2
  11. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/setup.py +2 -2
  12. fiddler-client-3.8.3/fiddler/VERSION +0 -1
  13. fiddler-client-3.8.3/fiddler/tests/test_utils.py +0 -99
  14. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/LICENSE.txt +0 -0
  15. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/MANIFEST.in +0 -0
  16. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/PUBLIC.md +0 -0
  17. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/README.md +0 -0
  18. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/__init__.py +0 -0
  19. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/configs.py +0 -0
  20. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/connection.py +0 -0
  21. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/constants/__init__.py +0 -0
  22. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/constants/alert_rule.py +0 -0
  23. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/constants/baseline.py +0 -0
  24. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/constants/common.py +0 -0
  25. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/constants/dataset.py +0 -0
  26. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/constants/events.py +0 -0
  27. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/constants/job.py +0 -0
  28. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/constants/model.py +0 -0
  29. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/constants/model_deployment.py +0 -0
  30. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/constants/xai.py +0 -0
  31. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/decorators.py +0 -0
  32. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/__init__.py +0 -0
  33. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/alert_record.py +0 -0
  34. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/alert_rule.py +0 -0
  35. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/base.py +0 -0
  36. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/baseline.py +0 -0
  37. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/custom_expression.py +0 -0
  38. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/dataset.py +0 -0
  39. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/events.py +0 -0
  40. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/file.py +0 -0
  41. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/job.py +0 -0
  42. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/model.py +0 -0
  43. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/model_artifact.py +0 -0
  44. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/model_deployment.py +0 -0
  45. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/organization.py +0 -0
  46. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/project.py +0 -0
  47. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/surrogate.py +0 -0
  48. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/user.py +0 -0
  49. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/entities/xai.py +0 -0
  50. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/exceptions.py +0 -0
  51. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/libs/__init__.py +0 -0
  52. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/libs/aws.py +0 -0
  53. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/libs/http_client.py +0 -0
  54. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/libs/json_encoder.py +0 -0
  55. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/libs/semver.py +0 -0
  56. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/packtools/__init__.py +0 -0
  57. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/packtools/gem.py +0 -0
  58. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/packtools/keras_ig_helpers.py +0 -0
  59. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/packtools/project_attributions_helpers.py +0 -0
  60. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/packtools/template_model.py +0 -0
  61. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/__init__.py +0 -0
  62. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/alert_record.py +0 -0
  63. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/alert_rule.py +0 -0
  64. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/base.py +0 -0
  65. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/baseline.py +0 -0
  66. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/custom_expression.py +0 -0
  67. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/custom_features.py +0 -0
  68. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/dataset.py +0 -0
  69. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/events.py +0 -0
  70. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/file.py +0 -0
  71. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/filter_query.py +0 -0
  72. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/job.py +0 -0
  73. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/model.py +0 -0
  74. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/model_deployment.py +0 -0
  75. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/model_schema.py +0 -0
  76. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/model_spec.py +0 -0
  77. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/model_task_params.py +0 -0
  78. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/organization.py +0 -0
  79. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/project.py +0 -0
  80. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/response.py +0 -0
  81. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/server_info.py +0 -0
  82. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/user.py +0 -0
  83. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/xai.py +0 -0
  84. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/schemas/xai_params.py +0 -0
  85. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/__init__.py +0 -0
  86. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/__init__.py +0 -0
  87. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_alert_record.py +0 -0
  88. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_alert_rule.py +0 -0
  89. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_baseline.py +0 -0
  90. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_custom_metric.py +0 -0
  91. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_dataset.py +0 -0
  92. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_events.py +0 -0
  93. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_files.py +0 -0
  94. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_generate_model.py +0 -0
  95. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_job.py +0 -0
  96. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_mixin.py +0 -0
  97. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_model.py +0 -0
  98. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_model_artifact.py +0 -0
  99. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_model_deployment.py +0 -0
  100. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_model_surrogate.py +0 -0
  101. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_project.py +0 -0
  102. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_segment.py +0 -0
  103. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_webhook.py +0 -0
  104. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/apis/test_xai.py +0 -0
  105. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/conftest.py +0 -0
  106. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/constants.py +0 -0
  107. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/test_connection.py +0 -0
  108. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/test_json_encoder.py +0 -0
  109. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/test_logger.py +0 -0
  110. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/test_sagemaker_auth.py +0 -0
  111. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/tests/utils.py +0 -0
  112. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/utils/__init__.py +0 -0
  113. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/utils/decorators.py +0 -0
  114. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/utils/helpers.py +0 -0
  115. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/utils/logger.py +0 -0
  116. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/utils/validations.py +0 -0
  117. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/utils/version.py +0 -0
  118. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler/version.py +0 -0
  119. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler_client.egg-info/dependency_links.txt +0 -0
  120. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/fiddler_client.egg-info/top_level.txt +0 -0
  121. {fiddler-client-3.8.3 → fiddler-client-3.9.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fiddler-client
3
- Version: 3.8.3
3
+ Version: 3.9.1
4
4
  Summary: Python client for Fiddler Platform
5
5
  Home-page: https://fiddler.ai
6
6
  Author: Fiddler Labs
@@ -0,0 +1 @@
1
+ 3.9.1
@@ -20,7 +20,7 @@ class Webhook(BaseEntity):
20
20
  """
21
21
  self.name = name
22
22
  self.url = url
23
- # provider is 'SLACK' or 'OTHER' as of Aug 2023.
23
+ # provider is 'SLACK', 'MS_TEAMS' as of May 2025.
24
24
  self.provider = provider
25
25
 
26
26
  self.id: UUID | None = None
@@ -113,7 +113,7 @@ class Webhook(BaseEntity):
113
113
 
114
114
  :params name: name of webhook
115
115
  :params url: webhook url
116
- :params provider: Either 'SLACK' or 'OTHER'
116
+ :params provider: Either 'SLACK' or 'MS_TEAMS'
117
117
 
118
118
  :returns: Created `Webhook` object.
119
119
  """
@@ -9,8 +9,10 @@ from fiddler.schemas.base import BaseModel
9
9
 
10
10
  @enum.unique
11
11
  class WebhookProvider(str, enum.Enum):
12
- # provider is 'SLACK' or 'OTHER' as of Aug 2023.
12
+ # provider is 'SLACK' or 'MS_TEAMS' as of May 2025.
13
+ # keeping OTHER in enum for existing webhooks for backwards compatibility
13
14
  SLACK = 'SLACK'
15
+ MS_TEAMS = 'MS_TEAMS'
14
16
  OTHER = 'OTHER'
15
17
 
16
18
 
@@ -0,0 +1,431 @@
1
+ import os
2
+ import tempfile
3
+ from pathlib import Path
4
+ import numpy as np
5
+ import pandas as pd
6
+ import pytest
7
+
8
+ from fiddler.constants.model import DataType
9
+ from fiddler.schemas.server_info import Version
10
+ from fiddler.utils.helpers import group_by, try_series_retype
11
+ from fiddler.utils.column_generator import create_columns_from_df
12
+ from fiddler.utils.validations import validate_artifact_dir
13
+ from fiddler.utils.version import match_semver
14
+
15
+ MODEL_DATA = [{'Unnamed: 0': 43660,
16
+ 'CreditScore': 681,
17
+ 'Geography': 'France',
18
+ 'Gender': 'Female',
19
+ 'Age': 45,
20
+ 'Tenure': 4,
21
+ 'Balance': 150461.78248572032,
22
+ 'NumOfProducts': 1,
23
+ 'HasCrCard': True,
24
+ 'IsActiveMember': True,
25
+ 'EstimatedSalary': 121476.50483532489,
26
+ 'Churned': 'Not Churned',
27
+ 'probability_churned': 0.2070909278091097},
28
+ {'Unnamed: 0': 87278,
29
+ 'CreditScore': 604,
30
+ 'Geography': 'Spain',
31
+ 'Gender': 'Male',
32
+ 'Age': 25,
33
+ 'Tenure': 8,
34
+ 'Balance': 870.563948633173,
35
+ 'NumOfProducts': 2,
36
+ 'HasCrCard': False,
37
+ 'IsActiveMember': True,
38
+ 'EstimatedSalary': 39030.86584360056,
39
+ 'Churned': 'Not Churned',
40
+ 'probability_churned': 0.0619905773329336},
41
+ {'Unnamed: 0': 14317,
42
+ 'CreditScore': 648,
43
+ 'Geography': 'France',
44
+ 'Gender': 'Female',
45
+ 'Age': 50,
46
+ 'Tenure': 6,
47
+ 'Balance': 127661.4013780268,
48
+ 'NumOfProducts': 1,
49
+ 'HasCrCard': True,
50
+ 'IsActiveMember': True,
51
+ 'EstimatedSalary': -8216.031548157727,
52
+ 'Churned': 'Churned',
53
+ 'probability_churned': 0.1026814066436183},
54
+ {'Unnamed: 0': 81932,
55
+ 'CreditScore': 670,
56
+ 'Geography': 'France',
57
+ 'Gender': 'Male',
58
+ 'Age': 49,
59
+ 'Tenure': 7,
60
+ 'Balance': 95199.48061342211,
61
+ 'NumOfProducts': 3,
62
+ 'HasCrCard': False,
63
+ 'IsActiveMember': True,
64
+ 'EstimatedSalary': 47114.97249302002,
65
+ 'Churned': 'Not Churned',
66
+ 'probability_churned': 0.2134035806234498},
67
+ {'Unnamed: 0': 95321,
68
+ 'CreditScore': 616,
69
+ 'Geography': 'Germany',
70
+ 'Gender': 'Female',
71
+ 'Age': 35,
72
+ 'Tenure': 1,
73
+ 'Balance': 88151.28675933508,
74
+ 'NumOfProducts': 1,
75
+ 'HasCrCard': True,
76
+ 'IsActiveMember': False,
77
+ 'EstimatedSalary': 107882.06957422369,
78
+ 'Churned': 'Churned',
79
+ 'probability_churned': 0.8824657828379177},
80
+ {'Unnamed: 0': 5405,
81
+ 'CreditScore': 536,
82
+ 'Geography': 'France',
83
+ 'Gender': 'Female',
84
+ 'Age': 41,
85
+ 'Tenure': 4,
86
+ 'Balance': 167680.40434914205,
87
+ 'NumOfProducts': 1,
88
+ 'HasCrCard': True,
89
+ 'IsActiveMember': True,
90
+ 'EstimatedSalary': 133588.33926550962,
91
+ 'Churned': 'Not Churned',
92
+ 'probability_churned': 0.1189636863349831},
93
+ {'Unnamed: 0': 33188,
94
+ 'CreditScore': 666,
95
+ 'Geography': 'Spain',
96
+ 'Gender': 'Male',
97
+ 'Age': 34,
98
+ 'Tenure': 2,
99
+ 'Balance': 120370.38776657668,
100
+ 'NumOfProducts': 2,
101
+ 'HasCrCard': False,
102
+ 'IsActiveMember': True,
103
+ 'EstimatedSalary': 60070.356958972116,
104
+ 'Churned': 'Not Churned',
105
+ 'probability_churned': 0.1244325159224678},
106
+ {'Unnamed: 0': 63421,
107
+ 'CreditScore': 613,
108
+ 'Geography': 'France',
109
+ 'Gender': 'Male',
110
+ 'Age': 35,
111
+ 'Tenure': 2,
112
+ 'Balance': 1632.9882268964022,
113
+ 'NumOfProducts': 2,
114
+ 'HasCrCard': True,
115
+ 'IsActiveMember': True,
116
+ 'EstimatedSalary': 77206.59807746019,
117
+ 'Churned': 'Not Churned',
118
+ 'probability_churned': 0.1037151191218035},
119
+ {'Unnamed: 0': 72897,
120
+ 'CreditScore': 591,
121
+ 'Geography': 'Germany',
122
+ 'Gender': 'Male',
123
+ 'Age': 85,
124
+ 'Tenure': 4,
125
+ 'Balance': 115817.11190439922,
126
+ 'NumOfProducts': 1,
127
+ 'HasCrCard': False,
128
+ 'IsActiveMember': False,
129
+ 'EstimatedSalary': 23310.677701467852,
130
+ 'Churned': 'Not Churned',
131
+ 'probability_churned': 0.2658220429852892},
132
+ {'Unnamed: 0': 9507,
133
+ 'CreditScore': 488,
134
+ 'Geography': 'Spain',
135
+ 'Gender': 'Female',
136
+ 'Age': 40,
137
+ 'Tenure': 8,
138
+ 'Balance': 101678.5269411524,
139
+ 'NumOfProducts': 1,
140
+ 'HasCrCard': True,
141
+ 'IsActiveMember': False,
142
+ 'EstimatedSalary': 9013.186551877934,
143
+ 'Churned': 'Not Churned',
144
+ 'probability_churned': 0.1167422545893403}
145
+ ]
146
+
147
+ def test_match_semvar_version() -> None:
148
+ assert match_semver(None, '>=22.9.0') is False
149
+ assert match_semver(Version.parse('22.9.0'), '>=22.10.0') is False
150
+ assert match_semver(Version.parse('22.10.0'), '>=22.10.0') is True
151
+ assert match_semver(Version.parse('22.10.0'), '>22.10.0') is False
152
+ assert match_semver(Version.parse('22.11.0'), '>=22.10.0') is True
153
+ assert match_semver(Version.parse('22.11.0'), '>22.10.0') is True
154
+ assert match_semver(Version.parse('22.10.0'), '<=22.10.0') is True
155
+ assert match_semver(Version.parse('22.10.0'), '<22.10.0') is False
156
+ assert match_semver(Version.parse('22.9.0'), '<22.10.0') is True
157
+ assert match_semver(Version.parse('22.11.0-RC1'), '>=22.11.0') is True
158
+
159
+
160
+ def test_validate_artifact_dir(tmp_path) -> None:
161
+ artifact_dir = os.path.join(Path(__file__).resolve().parent, 'artifact_test_dir')
162
+ assert validate_artifact_dir(Path(artifact_dir)) is None
163
+ # Test for artifact_dir not valid directory
164
+ with pytest.raises(ValueError):
165
+ validate_artifact_dir(Path('test'))
166
+ # Test for package.py file not found
167
+ mock_dir = tmp_path / 'test'
168
+ mock_dir.mkdir()
169
+ with pytest.raises(ValueError):
170
+ validate_artifact_dir(Path(mock_dir))
171
+
172
+
173
+ def test_group_by_helper() -> None:
174
+ df = pd.DataFrame(
175
+ [
176
+ {'col1': 1, 'col2': 'foo'},
177
+ {'col1': 2, 'col2': 'bar'},
178
+ {'col1': 3, 'col2': 'baz'},
179
+ {'col1': 3, 'col2': 'foo'},
180
+ ]
181
+ )
182
+
183
+ # with output_path
184
+ with tempfile.TemporaryDirectory() as temp_dir:
185
+ file_path = Path(temp_dir) / 'data.csv'
186
+
187
+ assert file_path.exists() is False
188
+ group_by(df=df, group_by_col='col2', output_path=file_path)
189
+ assert file_path.exists() is True
190
+
191
+ # without output path
192
+ grouped_df = group_by(df=df, group_by_col='col2')
193
+ assert grouped_df.equals(
194
+ pd.DataFrame(
195
+ [
196
+ {'col2': 'foo', 'col1': [1, 3]},
197
+ {'col2': 'bar', 'col1': [2]},
198
+ {'col2': 'baz', 'col1': [3]},
199
+ ]
200
+ )
201
+ )
202
+
203
+
204
+ def test_try_series_retype() -> None:
205
+ series = pd.Series([1, 2, 3], dtype='float')
206
+ series = try_series_retype(series, 'int')
207
+ assert series.dtype == 'int'
208
+
209
+ series = pd.Series([1, 2, None])
210
+ series = try_series_retype(series, 'int')
211
+ assert series.dtype == 'float'
212
+
213
+
214
+ def test_try_series_retype_str_or_unkown() -> None:
215
+ series = pd.Series(['HIGH', 'MEDIUM', '', None], dtype='str')
216
+ series = try_series_retype(series, 'str')
217
+ assert series.dtype == 'object'
218
+
219
+
220
+ def test_try_series_retype_timestamp() -> None:
221
+ series = pd.Series(
222
+ ['2023-11-12 09:15:32.23', '2023-12-11 09:15:32.45'], dtype='str'
223
+ )
224
+ series = try_series_retype(series, 'timestamp')
225
+ assert series.dtype == 'datetime64[ns]'
226
+
227
+
228
+ def test_try_series_retype_timestamp_error() -> None:
229
+ series = pd.Series(['2023-11-12 09:15:32.23', None], dtype='str')
230
+
231
+ with pytest.raises(TypeError):
232
+ try_series_retype(series, 'timestamp')
233
+
234
+
235
+ def test_create_columns_from_df() -> None:
236
+ # test with an empty dataframe
237
+ df = pd.DataFrame()
238
+ with pytest.raises(ValueError):
239
+ create_columns_from_df(df)
240
+
241
+ # test with a dataframe from dictionary
242
+ df = pd.DataFrame(MODEL_DATA)
243
+
244
+ df['Gender'] = df['Gender'].astype('category')
245
+ df['Churned'] = df['Churned'].astype('category')
246
+
247
+ model_schema = create_columns_from_df(df)
248
+ columns = model_schema.columns
249
+ assert len(columns) == 13
250
+ assert columns[0].name == 'Unnamed: 0'
251
+ assert columns[0].data_type == DataType.INTEGER
252
+
253
+ assert columns[1].name == 'CreditScore'
254
+ assert columns[1].data_type == DataType.INTEGER
255
+
256
+ assert columns[2].name == 'Geography'
257
+ assert columns[2].data_type == DataType.STRING
258
+
259
+ assert columns[3].name == 'Gender'
260
+ assert columns[3].data_type == DataType.CATEGORY
261
+
262
+ assert columns[4].name == 'Age'
263
+ assert columns[4].data_type == DataType.INTEGER
264
+
265
+ assert columns[5].name == 'Tenure'
266
+ assert columns[5].data_type == DataType.INTEGER
267
+
268
+ assert columns[6].name == 'Balance'
269
+ assert columns[6].data_type == DataType.FLOAT
270
+
271
+ assert columns[7].name == 'NumOfProducts'
272
+ assert columns[7].data_type == DataType.INTEGER
273
+
274
+ assert columns[8].name == 'HasCrCard'
275
+ assert columns[8].data_type == DataType.BOOLEAN
276
+
277
+ assert columns[9].name == 'IsActiveMember'
278
+ assert columns[9].data_type == DataType.BOOLEAN
279
+
280
+ assert columns[10].name == 'EstimatedSalary'
281
+ assert columns[10].data_type == DataType.FLOAT
282
+
283
+ assert columns[11].name == 'Churned'
284
+ assert columns[11].data_type == DataType.CATEGORY
285
+
286
+ assert columns[12].name == 'probability_churned'
287
+ assert columns[12].data_type == DataType.FLOAT
288
+
289
+ def test_numeric_type_casting():
290
+ # Create a DataFrame with various integer types
291
+ df = pd.DataFrame({
292
+ 'int8_col': pd.Series([1, 2, 3], dtype='int8'),
293
+ 'int16_col': pd.Series([1000, 2000, 3000], dtype='int16'),
294
+ 'int32_col': pd.Series([100000, 200000, 300000], dtype='int32'),
295
+ 'int64_col': pd.Series([10000000000, 20000000000, 30000000000], dtype='int64'),
296
+ 'uint8_col': pd.Series([1, 2, 3], dtype='uint8'),
297
+ 'uint16_col': pd.Series([1000, 2000, 3000], dtype='uint16'),
298
+ 'uint32_col': pd.Series([100000, 200000, 300000], dtype='uint32'),
299
+ 'uint64_col': pd.Series([10000000000, 20000000000, 30000000000], dtype='uint64'),
300
+
301
+ # Float types
302
+ 'float16_col': pd.Series([1.0, 2.0, 3.0], dtype='float16'),
303
+ 'float32_col': pd.Series([1.234, 2.345, 3.456], dtype='float32'),
304
+ 'float64_col': pd.Series([1.23456789, 2.34567890, 3.45678901], dtype='float64'),
305
+
306
+ # Control columns
307
+ 'string_col': pd.Series(['1', '2', '3'], dtype='string'),
308
+ 'bool_col': pd.Series([True, False, True], dtype='bool')
309
+ })
310
+
311
+ # Generate columns from DataFrame
312
+ columns = create_columns_from_df(df).columns
313
+
314
+ # Create a mapping of column names to their expected data types
315
+ expected_types = {
316
+ 'int8_col': DataType.INTEGER,
317
+ 'int16_col': DataType.INTEGER,
318
+ 'int32_col': DataType.INTEGER,
319
+ 'int64_col': DataType.INTEGER,
320
+ 'uint8_col': DataType.INTEGER,
321
+ 'uint16_col': DataType.INTEGER,
322
+ 'uint32_col': DataType.INTEGER,
323
+ 'uint64_col': DataType.INTEGER,
324
+
325
+ # Float columns
326
+ 'float16_col': DataType.FLOAT,
327
+ 'float32_col': DataType.FLOAT,
328
+ 'float64_col': DataType.FLOAT,
329
+
330
+ # control columns
331
+ 'string_col': DataType.STRING,
332
+ 'bool_col': DataType.BOOLEAN
333
+ }
334
+
335
+ # Verify each column's data type
336
+ for column in columns:
337
+ assert column.data_type == expected_types[column.name], \
338
+ f"Column {column.name} should be {expected_types[column.name]}, but got {column.data_type}"
339
+
340
+ # Verify min and max values for numeric columns
341
+ for col_name in df.select_dtypes(include=['int', 'float']).columns:
342
+ col = next(c for c in columns if c.name == col_name)
343
+ assert col.min == df[col_name].min(), \
344
+ f"Min value mismatch for {col_name}: expected {df[col_name].min()}, got {col.min}"
345
+ assert col.max == df[col_name].max(), \
346
+ f"Max value mismatch for {col_name}: expected {df[col_name].max()}, got {col.max}"
347
+
348
+ def test_integer_edge_cases():
349
+ # Test with empty DataFrame
350
+ empty_df = pd.DataFrame({
351
+ 'int_col': pd.Series([], dtype='int64')
352
+ })
353
+ columns = create_columns_from_df(empty_df).columns
354
+ assert len(columns) == 1
355
+ assert columns[0].data_type == DataType.INTEGER
356
+
357
+ # Test with NaN values
358
+ df_with_nan = pd.DataFrame({
359
+ 'int_col': pd.Series([1, 2, np.nan], dtype='float64') # NaN forces float dtype
360
+ })
361
+ columns = create_columns_from_df(df_with_nan).columns
362
+ assert len(columns) == 1
363
+ assert columns[0].data_type == DataType.FLOAT # Should be float due to NaN
364
+
365
+ # Test with mixed types
366
+ df_mixed = pd.DataFrame({
367
+ 'mixed_col': pd.Series([1, '2', 3], dtype='object')
368
+ })
369
+ columns = create_columns_from_df(df_mixed).columns
370
+ assert len(columns) == 1
371
+ assert columns[0].data_type == DataType.STRING # Should be string due to mixed types
372
+
373
+ def test_float_edge_cases():
374
+ # Test with NaN values
375
+ df_nan = pd.DataFrame({
376
+ 'float_nan': pd.Series([1.0, np.nan, 3.0], dtype='float64')
377
+ })
378
+ columns = create_columns_from_df(df_nan).columns
379
+ assert len(columns) == 1
380
+ assert columns[0].data_type == DataType.FLOAT
381
+ assert columns[0].min == 1.0
382
+ assert columns[0].max == 3.0
383
+
384
+ # Test with infinity
385
+ df_inf = pd.DataFrame({
386
+ 'float_inf': pd.Series([1.0, np.inf, -np.inf], dtype='float64')
387
+ })
388
+ columns = create_columns_from_df(df_inf).columns
389
+ assert len(columns) == 1
390
+ assert columns[0].data_type == DataType.FLOAT
391
+ assert columns[0].min == -np.inf
392
+ assert columns[0].max == np.inf
393
+
394
+ # Test with very small numbers
395
+ df_small = pd.DataFrame({
396
+ 'float_small': pd.Series([1e-10, 1e-20, 1e-30], dtype='float64')
397
+ })
398
+ columns = create_columns_from_df(df_small).columns
399
+ assert len(columns) == 1
400
+ assert columns[0].data_type == DataType.FLOAT
401
+ assert columns[0].min == 1e-30
402
+ assert columns[0].max == 1e-10
403
+
404
+ # Test with very large numbers
405
+ df_large = pd.DataFrame({
406
+ 'float_large': pd.Series([1e10, 1e20, 1e30], dtype='float64')
407
+ })
408
+ columns = create_columns_from_df(df_large).columns
409
+ assert len(columns) == 1
410
+ assert columns[0].data_type == DataType.FLOAT
411
+ assert columns[0].min == 1e10
412
+ assert columns[0].max == 1e30
413
+
414
+ def test_mixed_numeric_types():
415
+ # Test with mixed integer and float values
416
+ df_mixed = pd.DataFrame({
417
+ 'mixed_numeric': pd.Series([1, 2.0, 3], dtype='float64')
418
+ })
419
+ columns = create_columns_from_df(df_mixed).columns
420
+ assert len(columns) == 1
421
+ assert columns[0].data_type == DataType.FLOAT
422
+ assert columns[0].min == 1.0
423
+ assert columns[0].max == 3.0
424
+
425
+ # Test with mixed numeric and string values
426
+ df_mixed_str = pd.DataFrame({
427
+ 'mixed_str': pd.Series([1, '2.0', 3], dtype='object')
428
+ })
429
+ columns = create_columns_from_df(df_mixed_str).columns
430
+ assert len(columns) == 1
431
+ assert columns[0].data_type == DataType.STRING # Should be string due to mixed types
@@ -0,0 +1,68 @@
1
+ from typing import List
2
+ import pandas as pd
3
+ from fiddler.schemas.model_schema import Column, ModelSchema
4
+ from fiddler.constants.model import DataType
5
+
6
+
7
+ def create_columns_from_df(df: pd.DataFrame) -> ModelSchema:
8
+ """
9
+ Helper function to create Columns from a pandas DataFrame column dtypes.
10
+ timedelta, period, interval & object dtypes are converted to string.
11
+ Sparse dtypes are not handled.
12
+ Args:
13
+ df: Input pandas DataFrame
14
+
15
+ Returns:
16
+ ModelSchema
17
+ """
18
+ columns: List[Column] = []
19
+
20
+ # Check if the DataFrame is empty - a dataframe with columns but no rows is valid
21
+ if len(df.columns) == 0:
22
+ raise ValueError("Cannot create columns from an empty DataFrame")
23
+
24
+ for col_name, dtype in df.dtypes.items():
25
+ # Map pandas dtypes to Fiddler DataTypes
26
+ if pd.api.types.is_float_dtype(dtype):
27
+ data_type = DataType.FLOAT
28
+ col_min, col_max = float(df[col_name].min()), float(df[col_name].max())
29
+ col = Column(
30
+ name=col_name,
31
+ data_type=data_type,
32
+ min=col_min,
33
+ max=col_max
34
+ )
35
+ elif pd.api.types.is_integer_dtype(dtype):
36
+ data_type = DataType.INTEGER
37
+ col = Column(
38
+ name=col_name,
39
+ data_type=data_type,
40
+ min=df[col_name].min(),
41
+ max=df[col_name].max()
42
+ )
43
+ elif pd.api.types.is_bool_dtype(dtype):
44
+ col = Column(
45
+ name=col_name,
46
+ data_type=DataType.BOOLEAN
47
+ )
48
+ elif pd.api.types.is_datetime64_any_dtype(dtype):
49
+ col = Column(
50
+ name=col_name,
51
+ data_type=DataType.TIMESTAMP
52
+ )
53
+ elif pd.api.types.is_categorical_dtype(dtype):
54
+ col = Column(
55
+ name=col_name,
56
+ data_type=DataType.CATEGORY,
57
+ categories=df[col_name].cat.categories.tolist()
58
+ )
59
+ else:
60
+ # Default to string for other types
61
+ col = Column(
62
+ name=col_name,
63
+ data_type=DataType.STRING
64
+ )
65
+
66
+ columns.append(col)
67
+
68
+ return ModelSchema(columns=columns)
@@ -73,6 +73,7 @@ class ModelGenerator(ConnectionMixin):
73
73
  url='/v3/model-factory',
74
74
  data=payload,
75
75
  headers={'Content-Type': 'application/json'},
76
+ timeout=600,
76
77
  )
77
78
 
78
79
  return GenerateModelResp(**response.json()['data'])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fiddler-client
3
- Version: 3.8.3
3
+ Version: 3.9.1
4
4
  Summary: Python client for Fiddler Platform
5
5
  Home-page: https://fiddler.ai
6
6
  Author: Fiddler Labs
@@ -103,6 +103,7 @@ fiddler/tests/apis/test_segment.py
103
103
  fiddler/tests/apis/test_webhook.py
104
104
  fiddler/tests/apis/test_xai.py
105
105
  fiddler/utils/__init__.py
106
+ fiddler/utils/column_generator.py
106
107
  fiddler/utils/decorators.py
107
108
  fiddler/utils/helpers.py
108
109
  fiddler/utils/logger.py
@@ -1,7 +1,7 @@
1
- deprecated==1.2.15
1
+ deprecated==1.2.18
2
2
  pandas>=1.2.5
3
3
  pip>=21.0
4
- pyarrow>=7.0.0
4
+ pyarrow>=15.0.0
5
5
  pydantic>=1.10.17
6
6
  pyyaml
7
7
  requests-toolbelt
@@ -29,10 +29,10 @@ setuptools.setup(
29
29
  'requests-toolbelt',
30
30
  'pandas>=1.2.5',
31
31
  'pydantic>=1.10.17',
32
- 'deprecated==1.2.15',
32
+ 'deprecated==1.2.18',
33
33
  'tqdm',
34
34
  'simplejson>=3.17.0',
35
- 'pyarrow>=7.0.0',
35
+ 'pyarrow>=15.0.0',
36
36
  'pyyaml',
37
37
  'typing-extensions>=4.6.0,<5',
38
38
  ],
@@ -1 +0,0 @@
1
- 3.8.3
@@ -1,99 +0,0 @@
1
- import os
2
- import tempfile
3
- from pathlib import Path
4
-
5
- import pandas as pd
6
- import pytest
7
-
8
- from fiddler.schemas.server_info import Version
9
- from fiddler.utils.helpers import group_by, try_series_retype
10
- from fiddler.utils.validations import validate_artifact_dir
11
- from fiddler.utils.version import match_semver
12
-
13
-
14
- def test_match_semvar_version() -> None:
15
- assert match_semver(None, '>=22.9.0') is False
16
- assert match_semver(Version.parse('22.9.0'), '>=22.10.0') is False
17
- assert match_semver(Version.parse('22.10.0'), '>=22.10.0') is True
18
- assert match_semver(Version.parse('22.10.0'), '>22.10.0') is False
19
- assert match_semver(Version.parse('22.11.0'), '>=22.10.0') is True
20
- assert match_semver(Version.parse('22.11.0'), '>22.10.0') is True
21
- assert match_semver(Version.parse('22.10.0'), '<=22.10.0') is True
22
- assert match_semver(Version.parse('22.10.0'), '<22.10.0') is False
23
- assert match_semver(Version.parse('22.9.0'), '<22.10.0') is True
24
- assert match_semver(Version.parse('22.11.0-RC1'), '>=22.11.0') is True
25
-
26
-
27
- def test_validate_artifact_dir(tmp_path) -> None:
28
- artifact_dir = os.path.join(Path(__file__).resolve().parent, 'artifact_test_dir')
29
- assert validate_artifact_dir(Path(artifact_dir)) is None
30
- # Test for artifact_dir not valid directory
31
- with pytest.raises(ValueError):
32
- validate_artifact_dir(Path('test'))
33
- # Test for package.py file not found
34
- mock_dir = tmp_path / 'test'
35
- mock_dir.mkdir()
36
- with pytest.raises(ValueError):
37
- validate_artifact_dir(Path(mock_dir))
38
-
39
-
40
- def test_group_by_helper() -> None:
41
- df = pd.DataFrame(
42
- [
43
- {'col1': 1, 'col2': 'foo'},
44
- {'col1': 2, 'col2': 'bar'},
45
- {'col1': 3, 'col2': 'baz'},
46
- {'col1': 3, 'col2': 'foo'},
47
- ]
48
- )
49
-
50
- # with output_path
51
- with tempfile.TemporaryDirectory() as temp_dir:
52
- file_path = Path(temp_dir) / 'data.csv'
53
-
54
- assert file_path.exists() is False
55
- group_by(df=df, group_by_col='col2', output_path=file_path)
56
- assert file_path.exists() is True
57
-
58
- # without output path
59
- grouped_df = group_by(df=df, group_by_col='col2')
60
- assert grouped_df.equals(
61
- pd.DataFrame(
62
- [
63
- {'col2': 'foo', 'col1': [1, 3]},
64
- {'col2': 'bar', 'col1': [2]},
65
- {'col2': 'baz', 'col1': [3]},
66
- ]
67
- )
68
- )
69
-
70
-
71
- def test_try_series_retype() -> None:
72
- series = pd.Series([1, 2, 3], dtype='float')
73
- series = try_series_retype(series, 'int')
74
- assert series.dtype == 'int'
75
-
76
- series = pd.Series([1, 2, None])
77
- series = try_series_retype(series, 'int')
78
- assert series.dtype == 'float'
79
-
80
-
81
- def test_try_series_retype_str_or_unkown() -> None:
82
- series = pd.Series(['HIGH', 'MEDIUM', '', None], dtype='str')
83
- series = try_series_retype(series, 'str')
84
- assert series.dtype == 'object'
85
-
86
-
87
- def test_try_series_retype_timestamp() -> None:
88
- series = pd.Series(
89
- ['2023-11-12 09:15:32.23', '2023-12-11 09:15:32.45'], dtype='str'
90
- )
91
- series = try_series_retype(series, 'timestamp')
92
- assert series.dtype == 'datetime64[ns]'
93
-
94
-
95
- def test_try_series_retype_timestamp_error() -> None:
96
- series = pd.Series(['2023-11-12 09:15:32.23', None], dtype='str')
97
-
98
- with pytest.raises(TypeError):
99
- try_series_retype(series, 'timestamp')
File without changes
File without changes
File without changes