SuperModelingFactory 0.5.5__tar.gz → 0.5.6__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.
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/ODPS_Tool.py +64 -22
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/Proc_Compare.py +64 -17
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Feature/Weighted_Screen.py +5 -3
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/_common.py +57 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/credit_model.py +15 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/feature_validation.py +5 -7
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/field_meta.py +28 -1
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/reject_inference.py +200 -49
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/sample_analysis.py +17 -2
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/score_comparison.py +101 -13
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/__init__.py +1 -1
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/PKG-INFO +2 -2
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/README.md +1 -1
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/SuperModelingFactory.egg-info/PKG-INFO +2 -2
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/pyproject.toml +1 -1
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/setup.py +1 -1
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/ExcelMaster/ExcelFormatTool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/ExcelMaster/ExcelMaster.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/ExcelMaster/Template.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/ExcelMaster/Utility.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/ExcelMaster/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/LICENSE +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/MANIFEST.in +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/Binning_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/Binning_Tool.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/Check_DuckDB_Compatibility.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/Json_Data_Converter.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/Model_Registry_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/Parallel_Engine.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/Parallel_ODPS_Manager.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/Slope_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/Slope_Tool.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/XOR_Encryptor.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/XOR_Encryptor.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/kDataFrame.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/kDataFrame.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/sample_weight_utils.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/utils.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Eval/Evaluation_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Eval/Evaluation_Tool.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Eval/Model_Eval_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Eval/Model_Eval_Tool.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Eval/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Eval/evaluate_model.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Eval/evaluate_model.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Eval/weighted_eval_utils.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Explainability/Coalition_Structure.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Explainability/Model_Explainer.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Explainability/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Feature/Distribution_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Feature/Distribution_Tool.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Feature/Feature_Insights.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Feature/Feature_Insights.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Feature/Feature_Screen.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Feature/PSI_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Feature/PSI_Tool.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Feature/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Model/Backward_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Model/Backward_Tool.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Model/GBM_Search_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Model/GBM_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Model/GBM_Tool.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Model/LRM_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Model/LRM_Tool.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Model/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/mock_sample.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/orchestrator.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/score_consistency_uat.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/screening_artifact.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Sample/Distribution_Adaptation.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Sample/Distribution_Adaptation.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Sample/Reject_Infer.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Sample/Reject_Infer.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Sample/Sample_Split.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Sample/Sample_Split.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Sample/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/UAT/UAT_Consistency_Checker.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/UAT/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/WOE_Adapter.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/WOE_Adapter.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/WOE_Master.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/WOE_Master.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/WOE_Monotone_Binner.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/WOE_Plot_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/WOE_Report_Builder.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/WOE_Report_Builder.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/WOE_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/WOE_Tool.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/plot_woe_tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/WOE/plot_woe_tool.pyi +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/_utils/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/_utils/nan_guard.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/_utils/robust.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/_utils/sentinels.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/ref_font/KaiTi.ttf +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/ref_font/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/ref_font/simsun.ttc +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Report/Report_Tool.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Report/__init__.py +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/SuperModelingFactory.egg-info/SOURCES.txt +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/SuperModelingFactory.egg-info/dependency_links.txt +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/SuperModelingFactory.egg-info/not-zip-safe +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/SuperModelingFactory.egg-info/requires.txt +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/SuperModelingFactory.egg-info/top_level.txt +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/requirements.txt +0 -0
- {supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/setup.cfg +0 -0
|
@@ -281,17 +281,31 @@ class ODPSRunner(object):
|
|
|
281
281
|
|
|
282
282
|
@staticmethod
|
|
283
283
|
def cre_table_schema(df, partition_name=None):
|
|
284
|
-
|
|
285
|
-
dtypes = df.dtypes
|
|
286
|
-
isnum_dtypes = [pd.api.types.is_numeric_dtype(x) for x in dtypes]
|
|
287
|
-
isint_dtypes = [pd.api.types.is_integer_dtype(x) for x in dtypes]
|
|
288
|
-
|
|
289
284
|
table_columns = []
|
|
290
285
|
table_partitions = []
|
|
291
|
-
for
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
286
|
+
for col, dtype in df.dtypes.items():
|
|
287
|
+
if pd.api.types.is_complex_dtype(dtype) or pd.api.types.is_timedelta64_dtype(dtype):
|
|
288
|
+
raise TypeError(
|
|
289
|
+
f"Unsupported dtype for ODPS schema inference: column={col!r}, dtype={dtype!s}."
|
|
290
|
+
)
|
|
291
|
+
if pd.api.types.is_bool_dtype(dtype):
|
|
292
|
+
col_type = "boolean"
|
|
293
|
+
elif pd.api.types.is_integer_dtype(dtype):
|
|
294
|
+
col_type = "bigint"
|
|
295
|
+
elif pd.api.types.is_float_dtype(dtype):
|
|
296
|
+
col_type = "float" if str(dtype).lower() == "float32" else "double"
|
|
297
|
+
elif pd.api.types.is_datetime64_any_dtype(dtype):
|
|
298
|
+
col_type = "datetime"
|
|
299
|
+
elif (
|
|
300
|
+
pd.api.types.is_string_dtype(dtype)
|
|
301
|
+
or pd.api.types.is_object_dtype(dtype)
|
|
302
|
+
or isinstance(dtype, pd.CategoricalDtype)
|
|
303
|
+
):
|
|
304
|
+
col_type = "string"
|
|
305
|
+
else:
|
|
306
|
+
raise TypeError(
|
|
307
|
+
f"Unsupported dtype for ODPS schema inference: column={col!r}, dtype={dtype!s}."
|
|
308
|
+
)
|
|
295
309
|
if col == partition_name:
|
|
296
310
|
table_partitions.append(Partition(name=col, type=col_type))
|
|
297
311
|
else:
|
|
@@ -304,6 +318,28 @@ class ODPSRunner(object):
|
|
|
304
318
|
|
|
305
319
|
return table_schema
|
|
306
320
|
|
|
321
|
+
@staticmethod
|
|
322
|
+
def _to_odps_records(df):
|
|
323
|
+
"""Return Python records with pandas missing values converted to ODPS NULL."""
|
|
324
|
+
object_df = df.astype(object)
|
|
325
|
+
object_df = object_df.where(pd.notna(object_df), None)
|
|
326
|
+
return object_df.values.tolist()
|
|
327
|
+
|
|
328
|
+
def _execute_sql_blocking(self, sql):
|
|
329
|
+
"""Execute DDL and wait until the remote ODPS instance has completed."""
|
|
330
|
+
execute_sql = getattr(self.o, "execute_sql", None)
|
|
331
|
+
if callable(execute_sql):
|
|
332
|
+
return execute_sql(sql)
|
|
333
|
+
|
|
334
|
+
run_sql = getattr(self.o, "run_sql", None)
|
|
335
|
+
if not callable(run_sql):
|
|
336
|
+
raise AttributeError("ODPS client must provide execute_sql() or run_sql().")
|
|
337
|
+
instance = run_sql(sql)
|
|
338
|
+
wait_for_success = getattr(instance, "wait_for_success", None)
|
|
339
|
+
if callable(wait_for_success):
|
|
340
|
+
wait_for_success()
|
|
341
|
+
return instance
|
|
342
|
+
|
|
307
343
|
def upload_df(self, df, table_name, table_schema=None, partition=None, atomic=True):
|
|
308
344
|
"""上传数据集至mc中创建新表
|
|
309
345
|
|
|
@@ -328,9 +364,11 @@ class ODPSRunner(object):
|
|
|
328
364
|
legacy behaviour, e.g. for tables whose downstream readers expect
|
|
329
365
|
a specific object identity.
|
|
330
366
|
"""
|
|
367
|
+
upload_data = df.copy()
|
|
331
368
|
if table_schema is None:
|
|
332
|
-
|
|
333
|
-
table_schema = self.cre_table_schema(df=
|
|
369
|
+
upload_data.loc[:, "py_inserttime"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
370
|
+
table_schema = self.cre_table_schema(df=upload_data, partition_name=None)
|
|
371
|
+
records = self._to_odps_records(upload_data)
|
|
334
372
|
|
|
335
373
|
if not atomic:
|
|
336
374
|
# Legacy pre-0.4.2 non-atomic path — retained under an explicit
|
|
@@ -339,10 +377,10 @@ class ODPSRunner(object):
|
|
|
339
377
|
t = self.o.create_table(table_name, table_schema)
|
|
340
378
|
if bool(partition):
|
|
341
379
|
with t.open_writer(partition=partition, create_partition=True) as writer:
|
|
342
|
-
writer.write(
|
|
380
|
+
writer.write(records)
|
|
343
381
|
else:
|
|
344
382
|
with t.open_writer() as writer:
|
|
345
|
-
writer.write(
|
|
383
|
+
writer.write(records)
|
|
346
384
|
logger.info(f'<<<< 完成数据入表{table_name}: shape={df.shape} >>>>')
|
|
347
385
|
return
|
|
348
386
|
|
|
@@ -360,10 +398,10 @@ class ODPSRunner(object):
|
|
|
360
398
|
try:
|
|
361
399
|
if bool(partition):
|
|
362
400
|
with tmp_table.open_writer(partition=partition, create_partition=True) as writer:
|
|
363
|
-
writer.write(
|
|
401
|
+
writer.write(records)
|
|
364
402
|
else:
|
|
365
403
|
with tmp_table.open_writer() as writer:
|
|
366
|
-
writer.write(
|
|
404
|
+
writer.write(records)
|
|
367
405
|
except Exception:
|
|
368
406
|
try:
|
|
369
407
|
self.o.delete_table(tmp_name, if_exists=True)
|
|
@@ -379,7 +417,7 @@ class ODPSRunner(object):
|
|
|
379
417
|
target_exists = self.o.exist_table(table_name)
|
|
380
418
|
if target_exists:
|
|
381
419
|
try:
|
|
382
|
-
self.
|
|
420
|
+
self._execute_sql_blocking(f"ALTER TABLE {table_name} RENAME TO {old_rename_name};")
|
|
383
421
|
except Exception:
|
|
384
422
|
# Rename of the live target failed — do not touch it. Drop the
|
|
385
423
|
# tmp table so we don't leak it, then re-raise.
|
|
@@ -392,13 +430,13 @@ class ODPSRunner(object):
|
|
|
392
430
|
raise
|
|
393
431
|
|
|
394
432
|
try:
|
|
395
|
-
self.
|
|
433
|
+
self._execute_sql_blocking(f"ALTER TABLE {tmp_name} RENAME TO {target_rename_name};")
|
|
396
434
|
except Exception:
|
|
397
435
|
# tmp -> target rename failed. If we already moved the original,
|
|
398
436
|
# try to restore it so the caller is not left with a missing table.
|
|
399
437
|
if target_exists:
|
|
400
438
|
try:
|
|
401
|
-
self.
|
|
439
|
+
self._execute_sql_blocking(f"ALTER TABLE {old_name} RENAME TO {target_rename_name};")
|
|
402
440
|
logger.warning(
|
|
403
441
|
f"upload_df: swap of {tmp_name} -> {table_name} failed; restored original from {old_name}"
|
|
404
442
|
)
|
|
@@ -439,7 +477,9 @@ class ODPSRunner(object):
|
|
|
439
477
|
def _rename_partition(self, table_name, source_partition, target_partition):
|
|
440
478
|
src = _format_odps_partition_spec(_parse_odps_partition_spec(source_partition), quoted=True)
|
|
441
479
|
dst = _format_odps_partition_spec(_parse_odps_partition_spec(target_partition), quoted=True)
|
|
442
|
-
self.
|
|
480
|
+
self._execute_sql_blocking(
|
|
481
|
+
f"ALTER TABLE {table_name} PARTITION ({src}) RENAME TO PARTITION ({dst});"
|
|
482
|
+
)
|
|
443
483
|
|
|
444
484
|
def insert_df(self, df, table_name, overwrite=True, partition=None, atomic=True):
|
|
445
485
|
"""将数据集插入至mc已存在的表中.
|
|
@@ -457,7 +497,9 @@ class ODPSRunner(object):
|
|
|
457
497
|
"""
|
|
458
498
|
t = self.o.get_table(table_name)
|
|
459
499
|
if "py_inserttime" in t.schema and "py_inserttime" not in df.columns:
|
|
500
|
+
df = df.copy()
|
|
460
501
|
df.loc[:, "py_inserttime"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
502
|
+
records = self._to_odps_records(df)
|
|
461
503
|
|
|
462
504
|
if bool(partition):
|
|
463
505
|
if overwrite and atomic:
|
|
@@ -476,7 +518,7 @@ class ODPSRunner(object):
|
|
|
476
518
|
|
|
477
519
|
try:
|
|
478
520
|
with t.open_writer(partition=staging_partition, create_partition=True) as writer:
|
|
479
|
-
writer.write(
|
|
521
|
+
writer.write(records)
|
|
480
522
|
if target_exists:
|
|
481
523
|
self._rename_partition(table_name, partition, backup_partition)
|
|
482
524
|
self._rename_partition(table_name, staging_partition, partition)
|
|
@@ -510,10 +552,10 @@ class ODPSRunner(object):
|
|
|
510
552
|
if overwrite:
|
|
511
553
|
t.delete_partition(partition, if_exists=True)
|
|
512
554
|
with t.open_writer(partition=partition, create_partition=True) as writer:
|
|
513
|
-
writer.write(
|
|
555
|
+
writer.write(records)
|
|
514
556
|
else:
|
|
515
557
|
if overwrite:
|
|
516
558
|
t.truncate()
|
|
517
559
|
with t.open_writer() as writer:
|
|
518
|
-
writer.write(
|
|
560
|
+
writer.write(records)
|
|
519
561
|
logger.info('<<<< 完成数据入表: shape={0} >>>>'.format(df.shape))
|
{supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Core/Proc_Compare.py
RENAMED
|
@@ -38,6 +38,7 @@ class ProcCompareConfig:
|
|
|
38
38
|
numeric_tol: float = 1e-8
|
|
39
39
|
numeric_rtol: float = 0.0
|
|
40
40
|
datetime_tol_seconds: float = 0.0
|
|
41
|
+
datetime_cols: list[str] = field(default_factory=list)
|
|
41
42
|
per_column_tolerance: dict[str, Any] = field(default_factory=dict)
|
|
42
43
|
|
|
43
44
|
both_null_equal: bool = True
|
|
@@ -185,15 +186,27 @@ class ProcCompareEngine:
|
|
|
185
186
|
cols = sorted((set(left_df.columns) | set(right_df.columns)) - {_ROW_ID_COL})
|
|
186
187
|
rows = []
|
|
187
188
|
ignore = set(self.config.ignore_cols)
|
|
188
|
-
|
|
189
|
+
requested_compare_set = set(
|
|
190
|
+
self._resolve_compare_cols(left_df, right_df, key_cols, allow_missing=True)
|
|
191
|
+
)
|
|
189
192
|
for col in cols:
|
|
190
193
|
in_left = col in left_df.columns
|
|
191
194
|
in_right = col in right_df.columns
|
|
195
|
+
requested_for_compare = col in requested_compare_set
|
|
196
|
+
eligible_for_compare = bool(
|
|
197
|
+
requested_for_compare
|
|
198
|
+
and in_left
|
|
199
|
+
and in_right
|
|
200
|
+
and col not in key_cols
|
|
201
|
+
and col not in ignore
|
|
202
|
+
)
|
|
192
203
|
if col in key_cols:
|
|
193
204
|
role = "key"
|
|
205
|
+
elif not (in_left and in_right):
|
|
206
|
+
role = "schema_only"
|
|
194
207
|
elif col in ignore:
|
|
195
208
|
role = "ignored"
|
|
196
|
-
elif
|
|
209
|
+
elif eligible_for_compare:
|
|
197
210
|
role = "compare"
|
|
198
211
|
else:
|
|
199
212
|
role = "not_compared"
|
|
@@ -207,6 +220,8 @@ class ProcCompareEngine:
|
|
|
207
220
|
{
|
|
208
221
|
"column": col,
|
|
209
222
|
"role": role,
|
|
223
|
+
"requested_for_compare": bool(requested_for_compare),
|
|
224
|
+
"eligible_for_compare": bool(eligible_for_compare),
|
|
210
225
|
"in_left": bool(in_left),
|
|
211
226
|
"in_right": bool(in_right),
|
|
212
227
|
"dtype_left": str(left_df[col].dtype) if in_left else "",
|
|
@@ -239,6 +254,7 @@ class ProcCompareEngine:
|
|
|
239
254
|
right_df: pd.DataFrame,
|
|
240
255
|
key_cols: list[str],
|
|
241
256
|
schema_summary: pd.DataFrame | None = None,
|
|
257
|
+
keep_internal_stats: bool = False,
|
|
242
258
|
) -> ProcCompareResult:
|
|
243
259
|
left_df, left_dups = self._handle_duplicates(left_df, key_cols, self.config.left_name)
|
|
244
260
|
right_df, right_dups = self._handle_duplicates(right_df, key_cols, self.config.right_name)
|
|
@@ -349,7 +365,11 @@ class ProcCompareEngine:
|
|
|
349
365
|
return ProcCompareResult(
|
|
350
366
|
coverage_summary=coverage_summary,
|
|
351
367
|
schema_summary=schema_summary,
|
|
352
|
-
column_summary=
|
|
368
|
+
column_summary=(
|
|
369
|
+
column_summary
|
|
370
|
+
if keep_internal_stats
|
|
371
|
+
else self._strip_internal_columns(column_summary)
|
|
372
|
+
),
|
|
353
373
|
row_summary=row_summary,
|
|
354
374
|
cell_mismatches=cell_mismatches,
|
|
355
375
|
duplicate_key_summary=duplicate_key_summary,
|
|
@@ -370,7 +390,7 @@ class ProcCompareEngine:
|
|
|
370
390
|
diff = pd.Series(np.nan, index=left.index, dtype="float64")
|
|
371
391
|
abs_diff = pd.Series(np.nan, index=left.index, dtype="float64")
|
|
372
392
|
|
|
373
|
-
if self._should_compare_datetime(left, right):
|
|
393
|
+
if self._should_compare_datetime(col, left, right):
|
|
374
394
|
left_dt = pd.to_datetime(left, errors="coerce")
|
|
375
395
|
right_dt = pd.to_datetime(right, errors="coerce")
|
|
376
396
|
diff = (left_dt - right_dt).dt.total_seconds()
|
|
@@ -421,9 +441,28 @@ class ProcCompareEngine:
|
|
|
421
441
|
return False
|
|
422
442
|
return bool(left_num.notna().all() and right_num.notna().all())
|
|
423
443
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
444
|
+
def _should_compare_datetime(self, col: str, left: pd.Series, right: pd.Series) -> bool:
|
|
445
|
+
if pd.api.types.is_datetime64_any_dtype(left) or pd.api.types.is_datetime64_any_dtype(right):
|
|
446
|
+
return True
|
|
447
|
+
if col in self.config.datetime_cols:
|
|
448
|
+
return True
|
|
449
|
+
|
|
450
|
+
override = self.config.per_column_tolerance.get(col)
|
|
451
|
+
if isinstance(override, dict) and "datetime_tol_seconds" in override:
|
|
452
|
+
return True
|
|
453
|
+
if self.config.datetime_tol_seconds <= 0:
|
|
454
|
+
return False
|
|
455
|
+
if self._should_compare_numeric(left, right):
|
|
456
|
+
return False
|
|
457
|
+
|
|
458
|
+
values = pd.concat([left.dropna(), right.dropna()], ignore_index=True)
|
|
459
|
+
if values.empty:
|
|
460
|
+
return False
|
|
461
|
+
text = values.astype("string").str.strip()
|
|
462
|
+
if not text.str.match(r"^\d{4}[-/]\d{1,2}[-/]\d{1,2}(?:[ T].*)?$", na=False).all():
|
|
463
|
+
return False
|
|
464
|
+
parsed = pd.to_datetime(values, errors="coerce")
|
|
465
|
+
return bool(parsed.notna().all())
|
|
427
466
|
|
|
428
467
|
def _column_numeric_tol(self, col: str) -> tuple[float, float]:
|
|
429
468
|
override = self.config.per_column_tolerance.get(col)
|
|
@@ -547,7 +586,13 @@ class ProcCompareEngine:
|
|
|
547
586
|
right_part_df = pd.read_csv(right_part) if right_part.exists() else pd.DataFrame(columns=right_columns)
|
|
548
587
|
left_part_df = self._normalize_missing(left_part_df)
|
|
549
588
|
right_part_df = self._normalize_missing(right_part_df)
|
|
550
|
-
return self._compare_frames(
|
|
589
|
+
return self._compare_frames(
|
|
590
|
+
left_part_df,
|
|
591
|
+
right_part_df,
|
|
592
|
+
key_cols,
|
|
593
|
+
schema_summary=schema_summary,
|
|
594
|
+
keep_internal_stats=True,
|
|
595
|
+
)
|
|
551
596
|
|
|
552
597
|
def _partition_input(self, data: pd.DataFrame | str | Path, out_dir: Path, key_cols: list[str]) -> None:
|
|
553
598
|
if isinstance(data, pd.DataFrame):
|
|
@@ -589,21 +634,19 @@ class ProcCompareEngine:
|
|
|
589
634
|
]
|
|
590
635
|
)
|
|
591
636
|
|
|
592
|
-
cols = pd.concat(
|
|
637
|
+
cols = pd.concat(
|
|
638
|
+
[p.column_summary.assign(_partial=idx) for idx, p in enumerate(partials)],
|
|
639
|
+
ignore_index=True,
|
|
640
|
+
)
|
|
593
641
|
if len(cols):
|
|
594
|
-
cols["_diff_sum"] = pd.concat(
|
|
595
|
-
[
|
|
596
|
-
p.column_summary.assign(_partial=idx).get("_diff_sum", pd.Series(dtype=float))
|
|
597
|
-
for idx, p in enumerate(partials)
|
|
598
|
-
],
|
|
599
|
-
ignore_index=True,
|
|
600
|
-
) if "_diff_sum" in cols.columns else 0.0
|
|
601
642
|
grouped = cols.groupby("column", dropna=False).agg(
|
|
602
643
|
n_compared=("n_compared", "sum"),
|
|
603
644
|
n_equal=("n_equal", "sum"),
|
|
604
645
|
n_mismatch=("n_mismatch", "sum"),
|
|
605
646
|
n_one_side_null=("n_one_side_null", "sum"),
|
|
606
647
|
n_both_null=("n_both_null", "sum"),
|
|
648
|
+
_diff_sum=("_diff_sum", "sum"),
|
|
649
|
+
_diff_count=("_diff_count", "sum"),
|
|
607
650
|
max_abs_diff=("max_abs_diff", "max"),
|
|
608
651
|
).reset_index()
|
|
609
652
|
grouped["pct_mismatch"] = np.where(
|
|
@@ -611,7 +654,11 @@ class ProcCompareEngine:
|
|
|
611
654
|
grouped["n_mismatch"] / grouped["n_compared"] * 100,
|
|
612
655
|
0.0,
|
|
613
656
|
)
|
|
614
|
-
grouped["mean_diff"] = np.
|
|
657
|
+
grouped["mean_diff"] = np.where(
|
|
658
|
+
grouped["_diff_count"] > 0,
|
|
659
|
+
grouped["_diff_sum"] / grouped["_diff_count"],
|
|
660
|
+
np.nan,
|
|
661
|
+
)
|
|
615
662
|
column_summary = grouped[
|
|
616
663
|
[
|
|
617
664
|
"column",
|
{supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Feature/Weighted_Screen.py
RENAMED
|
@@ -402,12 +402,14 @@ def _missing_rate_for_series(
|
|
|
402
402
|
return 1.0
|
|
403
403
|
valid = int(values.notna().sum())
|
|
404
404
|
return 1.0 - float(valid) / float(total)
|
|
405
|
-
|
|
406
|
-
|
|
405
|
+
present = values.notna().to_numpy()
|
|
406
|
+
if pd.api.types.is_numeric_dtype(values):
|
|
407
|
+
arr = values.to_numpy(dtype=float, copy=False)
|
|
408
|
+
present &= np.isfinite(arr)
|
|
407
409
|
total_w = float(np.sum(w))
|
|
408
410
|
if total_w <= 0:
|
|
409
411
|
return 1.0
|
|
410
|
-
return 1.0 - float(np.sum(w[
|
|
412
|
+
return 1.0 - float(np.sum(w[present])) / total_w
|
|
411
413
|
|
|
412
414
|
|
|
413
415
|
def _apply_missing_rate_stage(
|
|
@@ -55,6 +55,63 @@ def as_list(value: Any | Iterable[Any] | None, default: list[Any] | None = None)
|
|
|
55
55
|
return list(value)
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
def normalize_group_specs(
|
|
59
|
+
value: Any,
|
|
60
|
+
*,
|
|
61
|
+
default_min_size: int | None = None,
|
|
62
|
+
) -> list[dict[str, Any]]:
|
|
63
|
+
"""Normalize supported Pipeline group-spec forms into named dictionaries."""
|
|
64
|
+
if value is None:
|
|
65
|
+
return []
|
|
66
|
+
|
|
67
|
+
if isinstance(value, Mapping):
|
|
68
|
+
raw_specs = []
|
|
69
|
+
for name, spec in value.items():
|
|
70
|
+
if isinstance(spec, Mapping):
|
|
71
|
+
item = dict(spec)
|
|
72
|
+
item.setdefault("name", str(name))
|
|
73
|
+
else:
|
|
74
|
+
item = {"name": str(name), "columns": as_list(spec)}
|
|
75
|
+
raw_specs.append(item)
|
|
76
|
+
else:
|
|
77
|
+
raw_specs = as_list(value)
|
|
78
|
+
|
|
79
|
+
normalized: list[dict[str, Any]] = []
|
|
80
|
+
seen_names: set[str] = set()
|
|
81
|
+
for idx, spec in enumerate(raw_specs):
|
|
82
|
+
if isinstance(spec, Mapping):
|
|
83
|
+
item = dict(spec)
|
|
84
|
+
columns = item.get("columns", item.get("cols"))
|
|
85
|
+
if columns is None:
|
|
86
|
+
raise ValueError(
|
|
87
|
+
f"group_specs entry {idx} must contain 'columns'; got keys {sorted(item)}"
|
|
88
|
+
)
|
|
89
|
+
columns = as_list(columns)
|
|
90
|
+
name = str(item.get("name") or "_x_".join(str(col) for col in columns))
|
|
91
|
+
min_size = item.get("min_size", default_min_size)
|
|
92
|
+
else:
|
|
93
|
+
columns = as_list(spec)
|
|
94
|
+
name = "_x_".join(str(col) for col in columns) or f"group_{idx}"
|
|
95
|
+
min_size = default_min_size
|
|
96
|
+
|
|
97
|
+
if not columns or any(not isinstance(col, str) or not col.strip() for col in columns):
|
|
98
|
+
raise ValueError(
|
|
99
|
+
f"group_specs entry {idx} must define a non-empty list of column names; "
|
|
100
|
+
f"got {columns!r}"
|
|
101
|
+
)
|
|
102
|
+
if name in seen_names:
|
|
103
|
+
raise ValueError(f"group_specs contains duplicate name {name!r}")
|
|
104
|
+
seen_names.add(name)
|
|
105
|
+
normalized.append(
|
|
106
|
+
{
|
|
107
|
+
"name": name,
|
|
108
|
+
"columns": [str(col) for col in columns],
|
|
109
|
+
"min_size": default_min_size if min_size is None else int(min_size),
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
return normalized
|
|
113
|
+
|
|
114
|
+
|
|
58
115
|
def merge_dict(base: Mapping[str, Any] | None, override: Mapping[str, Any] | None) -> dict[str, Any]:
|
|
59
116
|
merged = dict(base or {})
|
|
60
117
|
merged.update(dict(override or {}))
|
{supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/credit_model.py
RENAMED
|
@@ -929,6 +929,21 @@ class CreditModelPipeline:
|
|
|
929
929
|
base_params = dict(cfg.model_params.get("lr", {}))
|
|
930
930
|
standardize = bool(base_params.pop("standardize", False))
|
|
931
931
|
lr = LRMaster(params=base_params or None, standardize=standardize)
|
|
932
|
+
allowed_search_params = {
|
|
933
|
+
"objective",
|
|
934
|
+
"primary_set",
|
|
935
|
+
"gap_ref_sets",
|
|
936
|
+
"metric",
|
|
937
|
+
"refit",
|
|
938
|
+
"verbose",
|
|
939
|
+
}
|
|
940
|
+
unknown_search_params = sorted(set(cfg.lr_search_params) - allowed_search_params)
|
|
941
|
+
if unknown_search_params:
|
|
942
|
+
raise ValueError(
|
|
943
|
+
f"Unsupported lr_search_params keys: {unknown_search_params}. "
|
|
944
|
+
f"Allowed keys are {sorted(allowed_search_params)}. "
|
|
945
|
+
"LRMaster.grid_search_params uses holdout eval_sets and does not accept cv."
|
|
946
|
+
)
|
|
932
947
|
params = merge_dict(
|
|
933
948
|
{
|
|
934
949
|
"objective": "oot_gap_penalized",
|
|
@@ -16,6 +16,7 @@ from ._common import (
|
|
|
16
16
|
apply_woe_fit_query,
|
|
17
17
|
as_list,
|
|
18
18
|
make_dirs,
|
|
19
|
+
normalize_group_specs,
|
|
19
20
|
safe_to_csv,
|
|
20
21
|
split_oot_by_flag,
|
|
21
22
|
validate_woe_fit_query_columns,
|
|
@@ -992,13 +993,10 @@ class FeatureValidationPipeline:
|
|
|
992
993
|
def _group_specs(self, include_global: bool = True) -> dict[str, list[str]]:
|
|
993
994
|
cfg = self.config
|
|
994
995
|
if cfg.group_specs is not None:
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
cols = [spec] if isinstance(spec, str) else list(spec)
|
|
1000
|
-
specs["x".join(cols) or f"group_{idx}"] = cols
|
|
1001
|
-
return specs
|
|
996
|
+
return {
|
|
997
|
+
str(spec["name"]): list(spec["columns"])
|
|
998
|
+
for spec in normalize_group_specs(cfg.group_specs)
|
|
999
|
+
}
|
|
1002
1000
|
specs: dict[str, list[str]] = {}
|
|
1003
1001
|
if include_global:
|
|
1004
1002
|
specs["global"] = []
|
{supermodelingfactory-0.5.5 → supermodelingfactory-0.5.6}/Modeling_Tool/Pipeline/field_meta.py
RENAMED
|
@@ -567,9 +567,10 @@ def _build_pipeline_registry() -> dict[str, PipelineRegistryEntry]:
|
|
|
567
567
|
result_attrs=[
|
|
568
568
|
"label_coverage_summary",
|
|
569
569
|
"segment_bad_rate_summary",
|
|
570
|
+
"profile_summary",
|
|
570
571
|
"split_candidate_summary",
|
|
571
572
|
"split_recommendation",
|
|
572
|
-
"
|
|
573
|
+
"output_paths",
|
|
573
574
|
],
|
|
574
575
|
),
|
|
575
576
|
"mock_sample": PipelineRegistryEntry(
|
|
@@ -1029,6 +1030,17 @@ def validate_pipeline_config(pipeline_key: str, values: dict[str, Any] | Any) ->
|
|
|
1029
1030
|
errors.append("optuna_n_trials 必须 >= 1。")
|
|
1030
1031
|
if int(vals.get("optuna_n_trials", 5) or 0) < 5:
|
|
1031
1032
|
warnings.append("optuna_n_trials 建议至少为 5,过小的搜索轮数不稳定。")
|
|
1033
|
+
allowed_lr_search_params = {
|
|
1034
|
+
"objective", "primary_set", "gap_ref_sets", "metric", "refit", "verbose"
|
|
1035
|
+
}
|
|
1036
|
+
unknown_lr_search_params = sorted(
|
|
1037
|
+
set(vals.get("lr_search_params") or {}) - allowed_lr_search_params
|
|
1038
|
+
)
|
|
1039
|
+
if unknown_lr_search_params:
|
|
1040
|
+
errors.append(
|
|
1041
|
+
f"Unsupported lr_search_params keys: {unknown_lr_search_params}; "
|
|
1042
|
+
f"allowed keys are {sorted(allowed_lr_search_params)}."
|
|
1043
|
+
)
|
|
1032
1044
|
elif entry.key == "feature_validation":
|
|
1033
1045
|
has_batch_config = bool(vals.get("feature_batches")) or vals.get("feature_batch_size") is not None
|
|
1034
1046
|
if vals.get("feature_batch_size") is not None and int(vals["feature_batch_size"]) <= 0:
|
|
@@ -1057,6 +1069,21 @@ def validate_pipeline_config(pipeline_key: str, values: dict[str, Any] | Any) ->
|
|
|
1057
1069
|
errors.append("target_col 不能为空。")
|
|
1058
1070
|
if missing("score_cols") and missing("base_score"):
|
|
1059
1071
|
warnings.append("未配置 score_cols/base_score 时将依赖 Pipeline 自动探测分数字段。")
|
|
1072
|
+
if vals.get("group_specs") is not None:
|
|
1073
|
+
try:
|
|
1074
|
+
from ._common import normalize_group_specs
|
|
1075
|
+
|
|
1076
|
+
normalize_group_specs(vals["group_specs"])
|
|
1077
|
+
except (TypeError, ValueError) as exc:
|
|
1078
|
+
errors.append(f"Invalid group_specs: {exc}")
|
|
1079
|
+
for name, spec in (vals.get("cross_metrics") or {}).items():
|
|
1080
|
+
if not isinstance(spec, (list, tuple)) or len(spec) != 2:
|
|
1081
|
+
errors.append(
|
|
1082
|
+
f"cross_metrics[{name!r}] must be a two-item [column, aggregation] pair."
|
|
1083
|
+
)
|
|
1084
|
+
pairwise_agg = vals.get("pairwise_cross_agg_dict")
|
|
1085
|
+
if pairwise_agg is not None and not isinstance(pairwise_agg, dict):
|
|
1086
|
+
errors.append("pairwise_cross_agg_dict must be a {column: aggregation(s)} mapping.")
|
|
1060
1087
|
elif entry.key == "score_consistency_uat":
|
|
1061
1088
|
if missing("main_model_score_col"):
|
|
1062
1089
|
errors.append("main_model_score_col 不能为空。")
|