SuperModelingFactory 0.2.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.
Files changed (79) hide show
  1. ExcelMaster/ExcelFormatTool.py +487 -0
  2. ExcelMaster/ExcelMaster.py +917 -0
  3. ExcelMaster/Template.py +525 -0
  4. ExcelMaster/Utility.py +233 -0
  5. ExcelMaster/__init__.py +3 -0
  6. Modeling_Tool/Core/Binning_Tool.py +1608 -0
  7. Modeling_Tool/Core/Binning_Tool.pyi +48 -0
  8. Modeling_Tool/Core/Check_DuckDB_Compatibility.py +835 -0
  9. Modeling_Tool/Core/Json_Data_Converter.py +621 -0
  10. Modeling_Tool/Core/Model_Registry_Tool.py +199 -0
  11. Modeling_Tool/Core/ODPS_Tool.py +284 -0
  12. Modeling_Tool/Core/Slope_Tool.py +356 -0
  13. Modeling_Tool/Core/Slope_Tool.pyi +31 -0
  14. Modeling_Tool/Core/XOR_Encryptor.py +207 -0
  15. Modeling_Tool/Core/XOR_Encryptor.pyi +26 -0
  16. Modeling_Tool/Core/__init__.py +99 -0
  17. Modeling_Tool/Core/kDataFrame.py +228 -0
  18. Modeling_Tool/Core/kDataFrame.pyi +43 -0
  19. Modeling_Tool/Core/sample_weight_utils.py +77 -0
  20. Modeling_Tool/Core/utils.py +2672 -0
  21. Modeling_Tool/Eval/Evaluation_Tool.py +1452 -0
  22. Modeling_Tool/Eval/Evaluation_Tool.pyi +47 -0
  23. Modeling_Tool/Eval/Model_Eval_Tool.py +2548 -0
  24. Modeling_Tool/Eval/Model_Eval_Tool.pyi +37 -0
  25. Modeling_Tool/Eval/__init__.py +54 -0
  26. Modeling_Tool/Eval/evaluate_model.py +2008 -0
  27. Modeling_Tool/Eval/evaluate_model.pyi +50 -0
  28. Modeling_Tool/Eval/weighted_eval_utils.py +326 -0
  29. Modeling_Tool/Explainability/Coalition_Structure.py +305 -0
  30. Modeling_Tool/Explainability/Model_Explainer.py +743 -0
  31. Modeling_Tool/Explainability/__init__.py +24 -0
  32. Modeling_Tool/Feature/Distribution_Tool.py +509 -0
  33. Modeling_Tool/Feature/Distribution_Tool.pyi +42 -0
  34. Modeling_Tool/Feature/Feature_Insights.py +762 -0
  35. Modeling_Tool/Feature/Feature_Insights.pyi +33 -0
  36. Modeling_Tool/Feature/PSI_Tool.py +1195 -0
  37. Modeling_Tool/Feature/PSI_Tool.pyi +29 -0
  38. Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +355 -0
  39. Modeling_Tool/Feature/__init__.py +40 -0
  40. Modeling_Tool/Model/Backward_Tool.py +778 -0
  41. Modeling_Tool/Model/Backward_Tool.pyi +45 -0
  42. Modeling_Tool/Model/GBM_Search_Tool.py +251 -0
  43. Modeling_Tool/Model/GBM_Tool.py +1610 -0
  44. Modeling_Tool/Model/GBM_Tool.pyi +90 -0
  45. Modeling_Tool/Model/LRM_Tool.py +1198 -0
  46. Modeling_Tool/Model/LRM_Tool.pyi +47 -0
  47. Modeling_Tool/Model/__init__.py +61 -0
  48. Modeling_Tool/Sample/Distribution_Adaptation.py +131 -0
  49. Modeling_Tool/Sample/Distribution_Adaptation.pyi +30 -0
  50. Modeling_Tool/Sample/Reject_Infer.py +413 -0
  51. Modeling_Tool/Sample/Reject_Infer.pyi +43 -0
  52. Modeling_Tool/Sample/Sample_Split.py +520 -0
  53. Modeling_Tool/Sample/Sample_Split.pyi +43 -0
  54. Modeling_Tool/Sample/__init__.py +31 -0
  55. Modeling_Tool/UAT/UAT_Consistency_Checker.py +1180 -0
  56. Modeling_Tool/UAT/__init__.py +19 -0
  57. Modeling_Tool/WOE/WOE_Adapter.py +204 -0
  58. Modeling_Tool/WOE/WOE_Adapter.pyi +21 -0
  59. Modeling_Tool/WOE/WOE_Master.py +491 -0
  60. Modeling_Tool/WOE/WOE_Master.pyi +40 -0
  61. Modeling_Tool/WOE/WOE_Monotone_Binner.py +3324 -0
  62. Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +71 -0
  63. Modeling_Tool/WOE/WOE_Plot_Tool.py +919 -0
  64. Modeling_Tool/WOE/WOE_Plot_Tool.pyi +46 -0
  65. Modeling_Tool/WOE/WOE_Report_Builder.py +214 -0
  66. Modeling_Tool/WOE/WOE_Report_Builder.pyi +24 -0
  67. Modeling_Tool/WOE/WOE_Tool.py +1094 -0
  68. Modeling_Tool/WOE/WOE_Tool.pyi +41 -0
  69. Modeling_Tool/WOE/__init__.py +86 -0
  70. Modeling_Tool/WOE/plot_woe_tool.py +290 -0
  71. Modeling_Tool/WOE/plot_woe_tool.pyi +25 -0
  72. Modeling_Tool/__init__.py +176 -0
  73. Report/Report_Tool.py +380 -0
  74. Report/__init__.py +3 -0
  75. supermodelingfactory-0.2.0.dist-info/METADATA +268 -0
  76. supermodelingfactory-0.2.0.dist-info/RECORD +79 -0
  77. supermodelingfactory-0.2.0.dist-info/WHEEL +5 -0
  78. supermodelingfactory-0.2.0.dist-info/licenses/LICENSE +102 -0
  79. supermodelingfactory-0.2.0.dist-info/top_level.txt +3 -0
@@ -0,0 +1,199 @@
1
+ """Model artifact persistence helpers with metadata support.
2
+
3
+ This module provides the public ``save_model`` / ``load_model`` helpers used by
4
+ ``Modeling_Tool``. It stays backward compatible with legacy files that contain
5
+ only a raw joblib object while adding an SMF artifact envelope for model version
6
+ and deployment metadata.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import platform
11
+ from datetime import datetime, timezone
12
+ from typing import Any, Dict, Optional
13
+
14
+ import joblib
15
+
16
+ _ARTIFACT_MARKER = "__smf_model_artifact__"
17
+ _ARTIFACT_VERSION = "1.0"
18
+
19
+
20
+ def _get_smf_version():
21
+ """Resolve the installed SMF version without creating hard import cycles."""
22
+ try:
23
+ import Modeling_Tool
24
+ return getattr(Modeling_Tool, "__version__", None)
25
+ except Exception:
26
+ return None
27
+
28
+
29
+ def _model_class_name(model):
30
+ if model is None:
31
+ return None
32
+ return type(model).__name__
33
+
34
+
35
+ def _model_module_name(model):
36
+ if model is None:
37
+ return None
38
+ return type(model).__module__
39
+
40
+
41
+ def _as_list(value):
42
+ if value is None:
43
+ return None
44
+ if isinstance(value, str):
45
+ return [value]
46
+ try:
47
+ return list(value)
48
+ except TypeError:
49
+ return [value]
50
+
51
+
52
+ def _build_model_metadata(
53
+ model,
54
+ metadata: Optional[Dict[str, Any]] = None,
55
+ feature_cols=None,
56
+ woe_mapping_path: Optional[str] = None,
57
+ train_window: Optional[Dict[str, Any]] = None,
58
+ metrics: Optional[Dict[str, Any]] = None,
59
+ model_name: Optional[str] = None,
60
+ model_version: Optional[str] = None,
61
+ ):
62
+ """Build the standard metadata block stored in an SMF model artifact."""
63
+ base = {
64
+ "smf_version": _get_smf_version(),
65
+ "artifact_version": _ARTIFACT_VERSION,
66
+ "model_name": model_name,
67
+ "model_version": model_version,
68
+ "model_class": _model_class_name(model),
69
+ "model_module": _model_module_name(model),
70
+ "feature_cols": _as_list(feature_cols),
71
+ "woe_mapping_path": woe_mapping_path,
72
+ "train_window": train_window,
73
+ "metrics": metrics,
74
+ "created_at": datetime.now(timezone.utc).isoformat(),
75
+ "python_version": platform.python_version(),
76
+ "platform": platform.platform(),
77
+ }
78
+ if metadata:
79
+ # User-provided values intentionally override the generated defaults.
80
+ base.update(dict(metadata))
81
+ return base
82
+
83
+
84
+ def _is_smf_model_artifact(obj):
85
+ """Return True when *obj* follows the SMF artifact envelope schema."""
86
+ return isinstance(obj, dict) and obj.get(_ARTIFACT_MARKER) is True and "model" in obj
87
+
88
+
89
+ def make_model_artifact(
90
+ model,
91
+ metadata: Optional[Dict[str, Any]] = None,
92
+ feature_cols=None,
93
+ woe_mapping_path: Optional[str] = None,
94
+ train_window: Optional[Dict[str, Any]] = None,
95
+ metrics: Optional[Dict[str, Any]] = None,
96
+ model_name: Optional[str] = None,
97
+ model_version: Optional[str] = None,
98
+ ):
99
+ """Create an in-memory SMF model artifact without writing it to disk."""
100
+ artifact_metadata = _build_model_metadata(
101
+ model=model,
102
+ metadata=metadata,
103
+ feature_cols=feature_cols,
104
+ woe_mapping_path=woe_mapping_path,
105
+ train_window=train_window,
106
+ metrics=metrics,
107
+ model_name=model_name,
108
+ model_version=model_version,
109
+ )
110
+ return {
111
+ _ARTIFACT_MARKER: True,
112
+ "artifact_version": _ARTIFACT_VERSION,
113
+ "model": model,
114
+ "metadata": artifact_metadata,
115
+ }
116
+
117
+
118
+ def save_model(
119
+ model,
120
+ filename,
121
+ metadata: Optional[Dict[str, Any]] = None,
122
+ feature_cols=None,
123
+ woe_mapping_path: Optional[str] = None,
124
+ train_window: Optional[Dict[str, Any]] = None,
125
+ metrics: Optional[Dict[str, Any]] = None,
126
+ model_name: Optional[str] = None,
127
+ model_version: Optional[str] = None,
128
+ include_metadata: bool = True,
129
+ ):
130
+ """Save a model, optionally wrapped with standard SMF metadata.
131
+
132
+ Parameters
133
+ ----------
134
+ model : object
135
+ Model object to persist.
136
+ filename : str or path-like
137
+ Destination path.
138
+ metadata : dict, optional
139
+ Additional or overriding metadata fields.
140
+ feature_cols : list-like, optional
141
+ Training feature list.
142
+ woe_mapping_path : str, optional
143
+ Path to the WOE mapping table used by the model.
144
+ train_window : dict, optional
145
+ Training / validation / OOT sample window metadata.
146
+ metrics : dict, optional
147
+ Evaluation metrics such as AUC / KS by dataset.
148
+ model_name : str, optional
149
+ Business model name.
150
+ model_version : str, optional
151
+ Business model version.
152
+ include_metadata : bool, default True
153
+ If True, save an SMF artifact envelope. If False, save the raw model
154
+ object exactly like the legacy helper.
155
+
156
+ Returns
157
+ -------
158
+ int
159
+ 0 on success.
160
+ """
161
+ payload = model
162
+ if include_metadata:
163
+ payload = make_model_artifact(
164
+ model=model,
165
+ metadata=metadata,
166
+ feature_cols=feature_cols,
167
+ woe_mapping_path=woe_mapping_path,
168
+ train_window=train_window,
169
+ metrics=metrics,
170
+ model_name=model_name,
171
+ model_version=model_version,
172
+ )
173
+ joblib.dump(payload, filename)
174
+ return 0
175
+
176
+
177
+ def load_model(model_path, return_metadata: bool = False):
178
+ """Load a legacy model or an SMF model artifact.
179
+
180
+ By default this keeps backward compatibility and returns only the model
181
+ object. Set ``return_metadata=True`` to receive ``(model, metadata)``.
182
+ """
183
+ obj = joblib.load(model_path)
184
+ if _is_smf_model_artifact(obj):
185
+ model = obj["model"]
186
+ metadata = obj.get("metadata", {})
187
+ return (model, metadata) if return_metadata else model
188
+ return (obj, {}) if return_metadata else obj
189
+
190
+
191
+ def load_model_metadata(model_path):
192
+ """Load only metadata from an SMF model artifact.
193
+
194
+ Legacy raw-model files return an empty dict.
195
+ """
196
+ obj = joblib.load(model_path)
197
+ if _is_smf_model_artifact(obj):
198
+ return obj.get("metadata", {})
199
+ return {}
@@ -0,0 +1,284 @@
1
+ from datetime import datetime
2
+ import logging
3
+ import os
4
+ logger = logging.getLogger(__name__)
5
+ import pandas as pd
6
+ from odps import ODPS, options
7
+ from odps.models import Schema, Column, Partition
8
+
9
+ # Available only in newer pandas versions. Older Airflow images should skip it.
10
+ try:
11
+ pd.set_option('future.no_silent_downcasting', True)
12
+ except (KeyError, ValueError):
13
+ pass
14
+ pd.options.mode.chained_assignment = None # default='warn'
15
+
16
+ logging.basicConfig(level=logging.INFO, format="%(message)s")
17
+
18
+ class ODPSRunner(object):
19
+ """ODPS执行类
20
+ """
21
+ def __init__(self):
22
+ self.o = ODPS(
23
+ os.environ["ALIBABA_CLOUD_ACCESS_KEY_ID"],
24
+ os.environ["ALIBABA_CLOUD_ACCESS_KEY_SECRET"],
25
+ os.environ.get("ODPS_PROJECT", "mex_anls"),
26
+ endpoint=os.environ.get(
27
+ "ODPS_ENDPOINT",
28
+ "https://service.ap-southeast-1-vpc.maxcompute.aliyun-inc.com/api",
29
+ ),
30
+ )
31
+
32
+ options.retry_times = 6 # 请求重试次数
33
+ options.pool_maxsize = 200 # 连接池最大容量
34
+ options.connect_timeout = 3600 # 连接超时
35
+ options.read_timeout = 3600 # 读取超时
36
+
37
+
38
+ def run_sql(self, sql, to_df=True, n_process=1, csv_path=None):
39
+ """运行SQL并下载结果。
40
+
41
+ Parameters
42
+ ----------
43
+ sql : str
44
+ 单个 SQL 代码。
45
+ to_df : bool, default True
46
+ 是否把结果加载到内存中作为 ``pandas.DataFrame`` 返回。
47
+ 若 ``False``, 函数返回**空 DataFrame**, 但仍会下载数据(当 ``csv_path`` 被指定时)。
48
+ n_process : int, default 1
49
+ ``executor.open_reader().to_pandas`` 的并行进程数。
50
+ csv_path : str, default None
51
+ 把结果另存为本地 CSV 的路径。**与 ``to_df`` 互相独立**:
52
+ * 只设 ``csv_path`` → 下载 + 写 CSV, 不返回数据 (返回空 DataFrame)
53
+ * 只设 ``to_df=True`` → 下载 + 返回 DataFrame, 不写 CSV
54
+ * 两个都设 → 下载 + 返回 + 写 CSV
55
+ * 都不设 → 只跑 SQL 不下载 (用于 DDL/INSERT 等)
56
+
57
+ Returns
58
+ -------
59
+ pandas.DataFrame
60
+ 当 ``to_df=True`` 时返回完整数据;
61
+ 当 ``to_df=False`` 时返回空 DataFrame (用于占位).
62
+
63
+ Notes
64
+ -----
65
+ - **执行**阶段(execute_sql)只跑一次, 无重试.
66
+ - **下载**阶段(to_pandas + to_csv)最多重试 6 次, 适用于网络抖动.
67
+ - 当 SQL 返回列数 > 200 时, ``_patch_wide_schema_download`` 自动 patch ODPS Tunnel,
68
+ 防止 HTTP 414 (URI too long).
69
+
70
+ Examples
71
+ --------
72
+ >>> odps = ODPSRunner()
73
+ >>> df = odps.run_sql("SELECT * FROM dual LIMIT 10") # 仅 DataFrame
74
+ >>> df = odps.run_sql("SELECT * FROM dual LIMIT 10", csv_path="x.csv") # DataFrame + CSV
75
+ >>> _ = odps.run_sql("SELECT * FROM dual LIMIT 10", to_df=False, # 仅 CSV
76
+ ... csv_path="x.csv")
77
+ >>> _ = odps.run_sql("CREATE TABLE t AS SELECT 1") # 仅执行, 不下载
78
+ """
79
+ # 准备SQL
80
+ sqldesc = sql[:100]+"..." if len(sql)>100 else sql
81
+ logging.info(f"SQL: \n{sqldesc}")
82
+
83
+ # 运行SQL(只执行一次,不重试)
84
+ starttime = datetime.now()
85
+ logging.info(f' execute_sql: {starttime.strftime("%Y-%m-%d %H:%M:%S")}')
86
+ executor = self.o.execute_sql(sql)
87
+
88
+ # 决定是否需要下载: 至少满足 to_df=True 或 csv_path 不为空
89
+ should_download = bool(to_df) or bool(csv_path)
90
+ df = pd.DataFrame()
91
+
92
+ if should_download:
93
+ k = 6
94
+ for i in range(k):
95
+ orig_build = None
96
+ try:
97
+ logging.info(f' to_pandas: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
98
+ reader = executor.open_reader()
99
+ orig_build = self._patch_wide_schema_download(reader)
100
+ if n_process > 1:
101
+ df = reader.to_pandas(n_process=n_process)
102
+ else:
103
+ df = reader.to_pandas()
104
+ if bool(csv_path):
105
+ logging.info(f' to_csv: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
106
+ df.to_csv(csv_path)
107
+ break
108
+ except Exception as e:
109
+ logging.error(f' download failed [{i+1}/{k}]: {e}')
110
+ if i == k - 1:
111
+ endtime = datetime.now()
112
+ duration = round((endtime - starttime).total_seconds(), 1)
113
+ raise SystemError(
114
+ f' break: {endtime.strftime("%Y-%m-%d %H:%M:%S")} duration {duration}\n'
115
+ )
116
+ finally:
117
+ if orig_build is not None:
118
+ from odps.tunnel.instancetunnel import InstanceDownloadSession
119
+ InstanceDownloadSession._build_input_stream = orig_build
120
+
121
+ # 当用户显式要求不要 DataFrame 时, 主动释放引用, 节省内存
122
+ if not to_df:
123
+ df = pd.DataFrame()
124
+
125
+ endtime = datetime.now()
126
+ duration = round((endtime - starttime).total_seconds(), 1)
127
+ logging.info(f' done: {endtime.strftime("%Y-%m-%d %H:%M:%S")} duration {duration}\n')
128
+ return df
129
+
130
+ def download_table(self, table_name, partition=None, n_process=1, csv_path=None):
131
+ """读取表中数据至DataFrame
132
+
133
+ Parameters
134
+ ----------
135
+ table_name : str
136
+ 表名
137
+ partition : dict
138
+ 分区, 例如: 'dt=2022-01-01,taino=0'
139
+ n_process : int, default 1
140
+ 将查询数据转为pandas.DataFrame的进程数
141
+ csv_path : str
142
+ 查询数据保存至csv文件路径
143
+
144
+ Returns
145
+ -------
146
+ df: pandas.DataFrame
147
+ SQL查询数据结果
148
+ """
149
+ logging.info(f"Table: \n{table_name} {partition}")
150
+ starttime = datetime.now()
151
+ logging.info(f' to_pandas: {starttime.strftime("%Y-%m-%d %H:%M:%S")}')
152
+
153
+ t = self.o.get_table(table_name)
154
+ if bool(partition):
155
+ reader = t.open_reader(partition=partition)
156
+ else:
157
+ reader = t.open_reader()
158
+ if n_process > 1:
159
+ df = reader.to_pandas(n_process=10)
160
+ else:
161
+ df = reader.to_pandas()
162
+
163
+ if bool(csv_path):
164
+ logging.info(f' to_csv: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
165
+ df.to_csv(csv_path, index=False)
166
+
167
+ endtime = datetime.now()
168
+ duration = round((endtime - starttime).total_seconds(), 3)
169
+ logging.info(f' done shape={df.shape}: {endtime.strftime("%Y-%m-%d %H:%M:%S")} duration {duration}\n')
170
+
171
+ return df
172
+
173
+ @staticmethod
174
+ def _patch_wide_schema_download(reader, col_threshold=200):
175
+ """Prevent HTTP 414 when a query result has many columns.
176
+
177
+ ODPS Tunnel encodes column names as URL query params. With 200+ columns
178
+ the URI exceeds the server limit. We temporarily replace the class-level
179
+ _build_input_stream to omit the columns param, so the server returns all
180
+ columns without URL-based filtering.
181
+
182
+ Returns the original method so the caller can restore it in a finally block.
183
+ Returns None when no patch is needed (schema is narrow enough).
184
+ """
185
+ ds = getattr(reader, '_download_session', None)
186
+ if ds is None:
187
+ return None
188
+ schema = getattr(ds, 'schema', None)
189
+ if schema is None or len(schema.simple_columns) <= col_threshold:
190
+ return None
191
+
192
+ from odps.tunnel.instancetunnel import InstanceDownloadSession
193
+ orig_build = InstanceDownloadSession._build_input_stream
194
+
195
+ def _build_no_column_filter(self, start, count, compress=False, columns=None, arrow=False, raw_size=None):
196
+ return orig_build(self, start, count, compress=compress, columns=None, arrow=arrow, raw_size=raw_size)
197
+
198
+ InstanceDownloadSession._build_input_stream = _build_no_column_filter
199
+ logging.info(f' wide schema ({len(schema.simple_columns)} cols): patched tunnel to omit column filter from URL')
200
+ return orig_build
201
+
202
+ @staticmethod
203
+ def cre_table_schema(df, partition_name=None):
204
+
205
+ dtypes = df.dtypes
206
+ isnum_dtypes = [pd.api.types.is_numeric_dtype(x) for x in dtypes]
207
+ isint_dtypes = [pd.api.types.is_integer_dtype(x) for x in dtypes]
208
+
209
+ table_columns = []
210
+ table_partitions = []
211
+ for i in range(len(df.columns)):
212
+ col = df.columns[i]
213
+ col_type = "string" if not isnum_dtypes[i] else "float"
214
+ col_type = "bigint" if isint_dtypes[i] else col_type
215
+ if col == partition_name:
216
+ table_partitions.append(Partition(name=col, type=col_type))
217
+ else:
218
+ table_columns.append(Column(name=col, type=col_type))
219
+
220
+ if bool(table_partitions):
221
+ table_schema = Schema(columns=table_columns, partitions=table_partitions)
222
+ else:
223
+ table_schema = Schema(columns=table_columns)
224
+
225
+ return table_schema
226
+
227
+ def upload_df(self, df, table_name, table_schema=None, partition=None):
228
+ """上传数据集至mc中创建新表
229
+
230
+ Parameters
231
+ ----------
232
+ table_name: str
233
+ 表名
234
+ table_schema: odps.models.Schema
235
+ 表Schema
236
+ df: pandas.DataFrame
237
+ 数据集
238
+ partition: string
239
+ 保存分区
240
+ """
241
+ if table_schema is None:
242
+ df.loc[:, "py_inserttime"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
243
+ table_schema = self.cre_table_schema(df=df, partition_name=None)
244
+
245
+ self.o.delete_table(table_name, if_exists=True)
246
+ t = self.o.create_table(table_name, table_schema)
247
+
248
+ if bool(partition):
249
+ with t.open_writer(partition=partition, create_partition=True) as writer:
250
+ writer.write(df.values.tolist())
251
+ else:
252
+ with t.open_writer() as writer:
253
+ writer.write(df.values.tolist())
254
+ logger.info(f'<<<< 完成数据入表{table_name}: shape={df.shape} >>>>')
255
+
256
+ def insert_df(self, df, table_name, overwrite=True, partition=None):
257
+ """将数据集插入至mc已存在的表中.
258
+
259
+ Parameters
260
+ ----------
261
+ df: pandas.DataFrame
262
+ 数据集
263
+ table_name: str
264
+ 表名
265
+ overwrite: Bool, default True
266
+ 是否覆盖
267
+ partition: string, default None
268
+ 写入分区, 默认为None即无分区
269
+ """
270
+ t = self.o.get_table(table_name)
271
+ if "py_inserttime" in t.schema and "py_inserttime" not in df.columns:
272
+ df.loc[:, "py_inserttime"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
273
+
274
+ if bool(partition):
275
+ if overwrite:
276
+ t.delete_partition(partition, if_exists=True)
277
+ with t.open_writer(partition=partition, create_partition=True) as writer:
278
+ writer.write(df.values.tolist())
279
+ else:
280
+ if overwrite:
281
+ t.truncate()
282
+ with t.open_writer() as writer:
283
+ writer.write(df.values.tolist())
284
+ logger.info('<<<< 完成数据入表: shape={0} >>>>'.format(df.shape))