mdbq 1.1.1__tar.gz → 1.1.3__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 (47) hide show
  1. {mdbq-1.1.1 → mdbq-1.1.3}/PKG-INFO +1 -1
  2. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/aggregation/aggregation.py +83 -32
  3. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/aggregation/df_types.py +1 -1
  4. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/aggregation/mysql_types.py +5 -4
  5. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/aggregation/query_data.py +6 -6
  6. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/clean/data_clean.py +6 -2
  7. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/config/get_myconf.py +2 -2
  8. mdbq-1.1.3/mdbq/dataframe/converter.py +81 -0
  9. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/mongo/mongo.py +3 -3
  10. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/mysql/mysql.py +50 -34
  11. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq.egg-info/PKG-INFO +1 -1
  12. {mdbq-1.1.1 → mdbq-1.1.3}/setup.py +1 -1
  13. mdbq-1.1.1/mdbq/dataframe/converter.py +0 -112
  14. {mdbq-1.1.1 → mdbq-1.1.3}/README.txt +0 -0
  15. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/__init__.py +0 -0
  16. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/__version__.py +0 -0
  17. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/aggregation/__init__.py +0 -0
  18. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/aggregation/optimize_data.py +0 -0
  19. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/bdup/__init__.py +0 -0
  20. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/bdup/bdup.py +0 -0
  21. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/clean/__init__.py +0 -0
  22. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/company/__init__.py +0 -0
  23. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/company/copysh.py +0 -0
  24. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/config/__init__.py +0 -0
  25. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/config/products.py +0 -0
  26. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/config/set_support.py +0 -0
  27. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/config/update_conf.py +0 -0
  28. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/dataframe/__init__.py +0 -0
  29. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/log/__init__.py +0 -0
  30. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/log/mylogger.py +0 -0
  31. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/mongo/__init__.py +0 -0
  32. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/mysql/__init__.py +0 -0
  33. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/mysql/data_types_/345/215/263/345/260/206/345/210/240/351/231/244.py" +0 -0
  34. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/mysql/s_query.py +0 -0
  35. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/mysql/year_month_day.py +0 -0
  36. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/other/__init__.py +0 -0
  37. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/other/porxy.py +0 -0
  38. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/other/pov_city.py +0 -0
  39. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/other/ua_sj.py +0 -0
  40. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/pbix/__init__.py +0 -0
  41. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/pbix/pbix_refresh.py +0 -0
  42. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/pbix/refresh_all.py +0 -0
  43. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq/spider/__init__.py +0 -0
  44. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq.egg-info/SOURCES.txt +0 -0
  45. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq.egg-info/dependency_links.txt +0 -0
  46. {mdbq-1.1.1 → mdbq-1.1.3}/mdbq.egg-info/top_level.txt +0 -0
  47. {mdbq-1.1.1 → mdbq-1.1.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mdbq
3
- Version: 1.1.1
3
+ Version: 1.1.3
4
4
  Home-page: https://pypi.org/project/mdbsql
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com
@@ -23,6 +23,21 @@ import getpass
23
23
 
24
24
  warnings.filterwarnings('ignore')
25
25
  """
26
+ 建表规范:
27
+ 1. 先建 json 表,再批量上传数据;(非常重要)
28
+ 在初创数据表时, 如果有不同类报表,新版和旧版都要取一个文件,先创建数据表,再导其他数据;
29
+ 例如有的报表转化率是0%,数据类型会被识别为2位小数: decimal(10, 2),正常值应类似 0.43%,应保留4个小数, 创建类型为 decimal(10, 4)
30
+ 为了避免以上可能数据类型错误的情况,初创时要先检查一遍数据类型,确认没问题再导其他数据!
31
+ 即导一个表,然后删除数据库,但保留 mysql_types.json,并检查表的数据类型(有问题就手动改 json 文件),之后会按 json 的 types 上传数据;
32
+
33
+ 2. 数据库和数据表名如果有字母,必须使用小写,大写在建库后会自动变小写,再次上传数据会找不到数据库(macos和linux都有这种情况)
34
+ 3. 无论是数据库/表/列名还是值,尽量避免特殊字符或者表情符号,数据库/表/列名尽量都使用 `列名` 转义,避免错误
35
+ 4. 小数必须使用 decimal, 禁止 float 和 double, 因为计算精度差异,后续需要聚合数据时会引发很多问题
36
+ 5. 日期类型暂时全部用 DATETIME,使用 DATE 在后续可能会重复插入不能排重,因为 df 进来的数据, 日期是带时间的,而数据库中日期不含时间
37
+ 6. 目前小数自动适配类型转换,对于文本或者大数全部用 mediumtext, 因为部分表涉及爬虫数据,进来的字符长度未知,暂时统一 mediumtext 避免入库失败
38
+
39
+
40
+
26
41
  1. DatabaseUpdate: 程序用于对爬虫下载的原始数据进行清洗并入库;
27
42
  数据清洗主要包括对字段名的非法字符处理,对 df 中的非法值进行预处理;
28
43
  数据入库时会较检并更新本地 json 文件的 dtypes 信息;
@@ -91,8 +106,8 @@ class DatabaseUpdate:
91
106
  # df.replace(to_replace=['\\N'], value=0, regex=False, inplace=True) # 替换掉特殊字符
92
107
  # df.replace(to_replace=[''], value=0, regex=False, inplace=True)
93
108
  # df.fillna(0, inplace=True)
94
- db_name = '天猫数据2'
95
- collection_name = f'推广数据_{tg_name}'
109
+ db_name = '推广数据2'
110
+ collection_name = f'{tg_name}'
96
111
  if name.endswith('.csv') and '超级直播' in name:
97
112
  # 超级直播
98
113
  df = pd.read_csv(os.path.join(root, name), encoding=encoding, header=0, na_filter=False)
@@ -109,8 +124,8 @@ class DatabaseUpdate:
109
124
  shop_name = ''
110
125
  # df.replace(to_replace=['\\N'], value=0, regex=False, inplace=True) # 替换掉特殊字符
111
126
  # df.replace(to_replace=[''], value=0, regex=False, inplace=True)
112
- db_name = '天猫数据2'
113
- collection_name = '推广数据_超级直播'
127
+ db_name = '推广数据2'
128
+ collection_name = '超级直播'
114
129
  elif name.endswith('.xls') and '短直联投' in name:
115
130
  # 短直联投
116
131
  df = pd.read_excel(os.path.join(root, name), sheet_name=None, header=0)
@@ -119,8 +134,8 @@ class DatabaseUpdate:
119
134
  print(f'{name} 报表数据为空')
120
135
  continue
121
136
  # df.replace(to_replace=[''], value=0, regex=False, inplace=True)
122
- db_name = '天猫数据2'
123
- collection_name = '推广数据_短直联投'
137
+ db_name = '推广数据2'
138
+ collection_name = '短直联投'
124
139
  elif name.endswith('.xls') and '视频加速推广' in name:
125
140
  # 超级短视频
126
141
  df = pd.read_excel(os.path.join(root, name), sheet_name=None, header=0)
@@ -129,15 +144,15 @@ class DatabaseUpdate:
129
144
  print(f'{name} 报表数据为空')
130
145
  continue
131
146
  # df.replace(to_replace=[''], value=0, regex=False, inplace=True)
132
- db_name = '天猫数据2'
133
- collection_name = '推广数据_超级短视频'
147
+ db_name = '推广数据2'
148
+ collection_name = '超级短视频'
134
149
  if '人群报表汇总' in name:
135
150
  df = pd.read_csv(os.path.join(root, name), encoding='utf-8_sig', header=1, na_filter=False)
136
151
  if len(df) == 0:
137
152
  print(f'{name} 报表数据为空')
138
153
  continue
139
- db_name = '天猫数据2'
140
- collection_name = '天猫_达摩盘_DMP报表'
154
+ db_name = '推广数据2'
155
+ collection_name = '达摩盘_dmp报表'
141
156
  # ----------------- 推广报表 分割线 -----------------
142
157
  # ----------------- 推广报表 分割线 -----------------
143
158
  date01 = re.findall(r'(\d{4}-\d{2}-\d{2})_\d{4}-\d{2}-\d{2}', str(name))
@@ -162,7 +177,7 @@ class DatabaseUpdate:
162
177
  else '智能场景' if x == '智能场景(原万相台)'
163
178
  else x
164
179
  )
165
- db_name = '生意参谋数据2'
180
+ db_name = '生意参谋2'
166
181
  if '经营优势' in df['一级来源'].tolist(): # 新版流量
167
182
  if '数据周期' in df.columns.tolist():
168
183
  collection_name='店铺来源_月数据_新版'
@@ -185,7 +200,7 @@ class DatabaseUpdate:
185
200
  if date01[0] != date02[0]:
186
201
  data_lis = date01[0] + '_' + date02[0]
187
202
  df.insert(loc=1, column='数据周期', value=data_lis)
188
- db_name = '生意参谋数据2'
203
+ db_name = '生意参谋2'
189
204
  collection_name = '商品排行'
190
205
  elif name.endswith('.xls') and '参谋店铺整体日报' in name:
191
206
  # 自助取数,店铺日报
@@ -194,7 +209,7 @@ class DatabaseUpdate:
194
209
  print(f'{name} 报表数据为空')
195
210
  continue
196
211
  df.rename(columns={'统计日期': '日期'}, inplace=True)
197
- db_name = '生意参谋数据2'
212
+ db_name = '生意参谋2'
198
213
  collection_name = '自助取数_整体日报'
199
214
  elif name.endswith('.xls') and '参谋每日流量_自助取数_新版' in name:
200
215
  # 自助取数,每日流量
@@ -213,7 +228,7 @@ class DatabaseUpdate:
213
228
  else '智能场景' if x == '智能场景(原万相台)'
214
229
  else x
215
230
  )
216
- db_name = '生意参谋数据2'
231
+ db_name = '生意参谋2'
217
232
  collection_name = '自助取数_每日流量'
218
233
  elif name.endswith('.xls') and '商品sku' in name:
219
234
  # 自助取数,商品sku
@@ -227,7 +242,7 @@ class DatabaseUpdate:
227
242
  'SKU ID': 'sku id',
228
243
  '商品SKU': '商品sku',
229
244
  }, inplace=True)
230
- db_name = '生意参谋数据2'
245
+ db_name = '生意参谋2'
231
246
  collection_name = '自助取数_商品sku'
232
247
  elif name.endswith('.xls') and '参谋店铺流量来源(月)' in name:
233
248
  # 自助取数,月店铺流量来源
@@ -247,7 +262,7 @@ class DatabaseUpdate:
247
262
  else x
248
263
  )
249
264
  df['日期'] = df['数据周期'].apply(lambda x: re.findall('(.*) ~', x)[0])
250
- db_name = '生意参谋数据2'
265
+ db_name = '生意参谋2'
251
266
  collection_name = '自助取数_店铺流量_月数据'
252
267
  elif name.endswith('.csv') and 'baobei' in name:
253
268
  # 生意经宝贝指标日数据
@@ -340,7 +355,7 @@ class DatabaseUpdate:
340
355
  continue
341
356
  df.rename(columns={'场次ID': '场次id', '商品ID': '商品id'}, inplace=True)
342
357
  df['日期'] = df['支付时间'].apply(lambda x: x.strftime('%Y-%m-%d'))
343
- db_name = '生意参谋数据2'
358
+ db_name = '生意参谋2'
344
359
  collection_name = '直播间成交订单明细'
345
360
  elif name.endswith('.xlsx') and '直播间大盘数据' in name:
346
361
  # 直播间大盘数据
@@ -349,7 +364,7 @@ class DatabaseUpdate:
349
364
  print(f'{name} 报表数据为空')
350
365
  continue
351
366
  df.rename(columns={'统计日期': '日期'}, inplace=True)
352
- db_name = '生意参谋数据2'
367
+ db_name = '生意参谋2'
353
368
  collection_name = '直播间大盘数据'
354
369
  elif name.endswith('.xls') and '直播业绩-成交拆解' in name:
355
370
  # 直播业绩-成交拆解
@@ -358,7 +373,7 @@ class DatabaseUpdate:
358
373
  print(f'{name} 报表数据为空')
359
374
  continue
360
375
  df.rename(columns={'统计日期': '日期'}, inplace=True)
361
- db_name = '生意参谋数据2'
376
+ db_name = '生意参谋2'
362
377
  collection_name = '直播业绩'
363
378
  elif name.endswith('.csv') and '淘宝店铺数据' in name:
364
379
  df = pd.read_csv(os.path.join(root, name), encoding='utf-8_sig', header=0, na_filter=False)
@@ -370,11 +385,11 @@ class DatabaseUpdate:
370
385
  df = df[df['人群规模'] != '']
371
386
  if len(df) == 0:
372
387
  continue
373
- db_name = '天猫数据2'
388
+ db_name = '推广数据2'
374
389
  collection_name = '万相台_人群洞察'
375
390
  elif name.endswith('.csv') and '客户_客户概况_画像' in name:
376
391
  df = pd.read_csv(os.path.join(root, name), encoding='utf-8_sig', header=0, na_filter=False)
377
- db_name = '生意参谋数据2'
392
+ db_name = '生意参谋2'
378
393
  collection_name = '客户_客户概况_画像'
379
394
  elif name.endswith('.csv') and '市场排行_店铺' in name:
380
395
  df = pd.read_csv(os.path.join(root, name), encoding='utf-8_sig', header=0, na_filter=False)
@@ -556,7 +571,7 @@ class DatabaseUpdate:
556
571
  continue
557
572
  df = df[df['缩略图'] != '合计']
558
573
  db_name = '生意经2'
559
- collection_name = 'E3_零售明细统计'
574
+ collection_name = 'e3_零售明细统计'
560
575
 
561
576
  # 商品素材,必须保持放在最后处理
562
577
  elif name.endswith('xlsx'):
@@ -615,8 +630,8 @@ class DatabaseUpdate:
615
630
  # print(f'{name}/{sheet4} 跳过')
616
631
  continue
617
632
  df.insert(loc=1, column='报表类型', value=sheet4)
618
- db_name = '天猫数据2'
619
- collection_name = f'推广数据_品销宝_{sheet4}'
633
+ db_name = '推广数据2'
634
+ collection_name = f'品销宝_{sheet4}'
620
635
  self.datas.append(
621
636
  {
622
637
  '数据库名': db_name,
@@ -834,7 +849,7 @@ class DatabaseUpdate:
834
849
  return df
835
850
 
836
851
 
837
- def upload(path, db_name, collection_name):
852
+ def upload(path, db_name, collection_name, one_file=False):
838
853
  """ 上传一个文件夹到数据库 """
839
854
  username, password, host, port = get_myconf.select_config_values(
840
855
  target_service='home_lx',
@@ -874,12 +889,16 @@ def upload(path, db_name, collection_name):
874
889
  collection_name=collection_name,
875
890
  )
876
891
  # print(dtypes)
892
+ count = 0
893
+ for root, dirs, files in os.walk(path, topdown=False):
894
+ for name in files:
895
+ count += 1
896
+ i = 0 # 用来统计当前处理文件进度
877
897
  for root, dirs, files in os.walk(path, topdown=False):
878
898
  for name in files:
879
899
  if '~$' in name or '.DS' in name or '.localized' in name or 'baidu' in name:
880
900
  continue
881
901
  if name.endswith('.csv'):
882
- # print(name)
883
902
  try:
884
903
  df = pd.read_csv(os.path.join(root, name), encoding='utf-8_sig', header=0, na_filter=False)
885
904
  if len(df) == 0:
@@ -896,14 +915,45 @@ def upload(path, db_name, collection_name):
896
915
  df = df.astype(dtypes)
897
916
 
898
917
  # d.df_to_mongo(df=df, db_name=db_name, collection_name=collection_name)
899
- m.df_to_mysql(df=df, db_name=db_name, table_name=collection_name)
918
+ m.df_to_mysql(df=df, db_name=db_name, table_name=collection_name, filename=name, count=f'{i}/{count}')
900
919
  # nas.df_to_mysql(df=df, db_name=db_name, table_name=collection_name)
901
920
  except Exception as e:
902
921
  print(name, e)
922
+ i += 1
923
+ if one_file: # 给 file_dir 函数调用
924
+ break # 每个文件夹只上传一个文件
903
925
  if d.client:
904
926
  d.client.close() # 必须手动关闭数据库连接
905
927
 
906
928
 
929
+ def file_dir():
930
+ """
931
+ 按照文件记录对照表
932
+ 批量上传数据库
933
+ """
934
+ filename = '文件目录对照表.csv'
935
+ if platform.system() == 'Windows':
936
+ path = 'C:\\同步空间\\BaiduSyncdisk\\原始文件2'
937
+ else:
938
+ path = '/Users/xigua/数据中心/原始文件2'
939
+ df = pd.read_csv(os.path.join(path, filename), encoding='utf-8_sig', header=0, na_filter=False)
940
+ datas = df.to_dict('records') # 转字典
941
+ for data in datas:
942
+ # print(data)
943
+ if data['入库进度'] == 0:
944
+ sub_path, db_name, collection_name = data['子文件夹'], data['数据库名'], data['数据表']
945
+ # print(os.path.join(path, sub_path), db_name, collection_name)
946
+ upload(
947
+ path=dir_path,
948
+ db_name=db_name,
949
+ collection_name=collection_name,
950
+ one_file=True
951
+ )
952
+ data.update({'入库进度': 1}) # 更新进度
953
+ df = pd.DataFrame.from_dict(datas, orient='columns')
954
+ df.to_csv(os.path.join(path, filename), encoding='utf-8_sig', index=False, header=True)
955
+
956
+
907
957
  def main():
908
958
  d = DatabaseUpdate(path='/Users/xigua/Downloads')
909
959
  d.new_unzip(is_move=True)
@@ -919,8 +969,9 @@ def main():
919
969
  if __name__ == '__main__':
920
970
  # username, password, host, port = get_myconf.select_config_values(target_service='nas', database='mysql')
921
971
  # print(username, password, host, port)
922
- upload(
923
- path='/Users/xigua/数据中心/原始文件2/属性设置/商品素材',
924
- db_name = '属性设置2',
925
- collection_name = '商品素材导出',
926
- )
972
+ # upload(
973
+ # path='/Users/xigua/数据中心/原始文件2/生意经/E3零售明细统计',
974
+ # db_name = '生意经2',
975
+ # collection_name = 'e3_零售明细统计',
976
+ # )
977
+ file_dir()
@@ -80,7 +80,7 @@ class DataTypes:
80
80
  self.datas.update(dtypes)
81
81
  return self.datas[db_name][collection_name]
82
82
  else: # 存在则读取,并更新 df 的 dtypes
83
- if db_name in list(self.datas.keys()): # ['京东数据2', '天猫数据2', '生意参谋数据2', '生意经2']
83
+ if db_name in list(self.datas.keys()): # ['京东数据2', '推广数据2', '生意参谋2', '生意经2']
84
84
  if collection_name in list(self.datas[db_name].keys()):
85
85
  if is_file_dtype: # 旧数据优先
86
86
  # # 用 dtypes 更新, 允许手动指定 json 文件里面的数据类型
@@ -67,7 +67,7 @@ class DataTypes:
67
67
  def get_mysql_types(self, cl, dtypes, db_name, table_name, is_file_dtype=True):
68
68
  """ 更新 mysql 的 types 信息到 json 文件 """
69
69
  if cl in self.datas.keys():
70
- if db_name in list(self.datas[cl].keys()): # ['京东数据2', '天猫数据2', '生意参谋数据2', '生意经2']
70
+ if db_name in list(self.datas[cl].keys()): # ['京东数据2', '推广数据2', '生意参谋2', '生意经2']
71
71
  if table_name in list(self.datas[cl][db_name].keys()):
72
72
  if is_file_dtype: # 旧数据优先
73
73
  # # 用 dtypes 更新, 允许手动指定 json 文件里面的数据类型
@@ -178,14 +178,14 @@ def mysql_all_dtypes(db_name=None, table_name=None, path=None):
178
178
 
179
179
  # db_name_lists = [
180
180
  # '京东数据2',
181
- # '天猫数据2',
181
+ # '推广数据2',
182
182
  # '市场数据2',
183
- # '生意参谋数据2',
183
+ # '生意参谋2',
184
184
  # '生意经2',
185
185
  # '属性设置2',
186
186
  # '聚合数据',
187
187
  # ]
188
- results = []
188
+ results = [] # 返回结果示例: [{'云电影': '电影更新'}, {'生意经2': 'e3_零售明细统计'}]
189
189
  for db_ in db_name_lists:
190
190
  config.update({'database': db_}) # 添加更新 config 字段
191
191
  connection = pymysql.connect(**config) # 连接数据库
@@ -233,6 +233,7 @@ def mysql_all_dtypes(db_name=None, table_name=None, path=None):
233
233
  )
234
234
  else:
235
235
  print(f'数据库回传数据(name_type)为空')
236
+ # print(d.datas)
236
237
  d.as_json_file()
237
238
 
238
239
  if __name__ == '__main__':
@@ -48,8 +48,8 @@ class MongoDatasQuery:
48
48
  '直接成交金额': 1,
49
49
  }
50
50
  df = self.download.data_to_df(
51
- db_name='天猫数据2',
52
- collection_name='推广数据_宝贝主体报表',
51
+ db_name='推广数据2',
52
+ collection_name='宝贝主体报表',
53
53
  projection=projection,
54
54
  )
55
55
  return df
@@ -100,8 +100,8 @@ class MysqlDatasQuery:
100
100
  '直接成交金额': 1,
101
101
  }
102
102
  df = self.download.data_to_df(
103
- db_name='天猫数据2',
104
- table_name='推广数据_宝贝主体报表',
103
+ db_name='推广数据2',
104
+ table_name='宝贝主体报表',
105
105
  start_date=start_date,
106
106
  end_date=end_date,
107
107
  projection=projection,
@@ -377,7 +377,7 @@ class GroupBy:
377
377
  def performance(self, bb_tg=True):
378
378
  # print(self.data_tgyj)
379
379
  tg, syj, idbm, pic, cost = (
380
- self.data_tgyj['推广数据_宝贝主体报表'],
380
+ self.data_tgyj['宝贝主体报表'],
381
381
  self.data_tgyj['天猫生意经_宝贝指标'],
382
382
  self.data_tgyj['商品id编码表'],
383
383
  self.data_tgyj['商品id图片对照表'],
@@ -502,7 +502,7 @@ def data_aggregation(service_databases=[{}]):
502
502
  data_dict = [
503
503
  {
504
504
  '数据库名': '聚合数据',
505
- '集合名': '推广数据_宝贝主体报表',
505
+ '集合名': '宝贝主体报表',
506
506
  '数据主体': sdq.tg_wxt(),
507
507
  },
508
508
  {
@@ -1113,8 +1113,12 @@ class DataClean:
1113
1113
  t_path = str(pathlib.Path(self.source_path, '京东报表/JD商家榜单'))
1114
1114
  bib(t_path, _as_month=True)
1115
1115
  elif name.endswith('.csv') and '导出-批量任务' in name:
1116
- t_path = str(pathlib.Path(self.source_path, '京东报表/商品信息导出'))
1117
- bib(t_path, _as_month=False)
1116
+ if 'SKU' in name:
1117
+ t_path = str(pathlib.Path(self.source_path, '京东报表/商品信息导出/sku'))
1118
+ bib(t_path, _as_month=False)
1119
+ elif 'SPU' in name:
1120
+ t_path = str(pathlib.Path(self.source_path, '京东报表/商品信息导出/spu'))
1121
+ bib(t_path, _as_month=False)
1118
1122
  elif name.endswith('.csv') and '_行业分析_竞争分析' in name:
1119
1123
  t_path = str(pathlib.Path(self.source_path, '京东报表/行业竞争分析'))
1120
1124
  bib(t_path, _as_month=True)
@@ -62,7 +62,7 @@ def select_config_values(target_service, database, path=None):
62
62
  if socket.gethostname() == 'xigua_lx':
63
63
  # 本机自身运行使用 127.0.0.1
64
64
  options = ['username_mysql_lx_nw', 'password_mysql_lx_nw', 'host_bd', 'port_mysql_lx_nw',]
65
- elif socket.gethostname() == 'xigua1' or socket.gethostname() == 'macbook pro':
65
+ elif socket.gethostname() == 'xigua1' or socket.gethostname() == 'MacBook-Pro':
66
66
  # 内网地址:正在运行的是 家里笔记本或者台式机,或者 macb ook pro
67
67
  options = ['username_mysql_lx_nw', 'password_mysql_lx_nw', 'host_mysql_lx_nw', 'port_mysql_lx_nw',]
68
68
  else:
@@ -126,6 +126,6 @@ def main():
126
126
 
127
127
  if __name__ == '__main__':
128
128
  # main()
129
- r, d, s, g = select_config_values(target_service='home_lx', database='mongodb')
129
+ r, d, s, g = select_config_values(target_service='home_lx', database='mysql')
130
130
  print(r, d, s, g, type(r), type(d), type(s), type(g))
131
131
  print(f'本机: {platform.system()} // {socket.gethostname()}')
@@ -0,0 +1,81 @@
1
+ # -*- coding:utf-8 -*-
2
+ import pandas as pd
3
+ import numpy as np
4
+ from decimal import Decimal
5
+ import re
6
+
7
+
8
+ class DataFrameConverter(object):
9
+ def __init__(self, df=pd.DataFrame({})):
10
+ self.df = df
11
+
12
+ def convert_df_cols(self, df=pd.DataFrame({})):
13
+ """
14
+ 清理 dataframe 非法值
15
+ 对数据类型进行转换(尝试将 object 类型转为 int 或 float)
16
+ """
17
+ if len(df) == 0:
18
+ df = self.df
19
+ if len(df) == 0:
20
+ return
21
+
22
+ def find_longest_decimal_value(number_list):
23
+ # 取列表中小数位数最长的值
24
+ longest_value = None
25
+ max_decimals = 0
26
+ for num in number_list:
27
+ decimal_places = len(str(num).split('.')[1])
28
+ if decimal_places > max_decimals:
29
+ max_decimals = decimal_places
30
+ longest_value = num
31
+ return longest_value
32
+
33
+ # dtypes = df.dtypes.apply(str).to_dict() # 将 dataframe 数据类型转为字典形式
34
+ df.replace([np.inf, -np.inf], 0, inplace=True) # 清理一些非法值
35
+ df.replace(to_replace=['\\N', '-', '--', '', 'nan'], value=0, regex=False, inplace=True) # 替换掉特殊字符
36
+ df.replace(to_replace=[','], value='', regex=True, inplace=True)
37
+ df.replace(to_replace=['="'], value='', regex=True, inplace=True) # ="和"不可以放在一起清洗, 因为有: id=86785565
38
+ df.replace(to_replace=['"'], value='', regex=True, inplace=True)
39
+ cols = df.columns.tolist()
40
+
41
+ for col in cols:
42
+ # 百分比在某些数据库中不兼容, 转换百分比为小数
43
+ df[col] = df[col].apply(lambda x: float(float((str(x).rstrip("%"))) / 100) if str(x).endswith('%') and '~' not in str(x) else x)
44
+ # 尝试转换合适的数据类型
45
+ if df[col].dtype == 'object':
46
+ try:
47
+ df[col] = df[col].apply(lambda x: int(x) if '_' not in str(x) else x)
48
+ except:
49
+ try:
50
+ df[col] = df[col].apply(lambda x: float(x) if '_' not in str(x) else x)
51
+ except:
52
+ pass
53
+ if df[col].dtype == 'float' or df[col].dtype == 'float64': # 对于小数类型, 保留 6 位小数
54
+ df[col] = df[col].fillna(0.0).apply(lambda x: round(x, 6))
55
+ # df[col] = df[col].fillna(0.0).apply(lambda x: "{:.6f}".format(x))
56
+ # df[col] = df[col].apply('float64')
57
+
58
+ # 转换日期样式的列为日期类型
59
+ value = df.loc[0, col]
60
+ if value:
61
+ res = re.match(r'\d{4}-\d{2}-\d{2}|\d{4}-\d{2}-\d{2} |\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}', str(value))
62
+ if res:
63
+ try:
64
+ df[col] = df[col].apply(lambda x: pd.to_datetime(x))
65
+ except:
66
+ pass
67
+ new_col = col.lower()
68
+ df.rename(columns={col: new_col}, inplace=True)
69
+ df.fillna(0, inplace=True)
70
+ return df
71
+
72
+
73
+ if __name__ == '__main__':
74
+ # df = pd.DataFrame(np.random.randn(5, 3), columns=['a', 'b', 'c'])
75
+ # converter = DataFrameConverter()
76
+ # df = converter.convert_df_cols(df)
77
+ # print(df['a'].dtype)
78
+ # print(df)
79
+ pattern = 'dfa_dfawr__'
80
+ pattern = re.sub(r'_+$', '', pattern)
81
+ print(pattern)
@@ -658,7 +658,7 @@ class OptimizeDatas:
658
658
  def rename_column(self):
659
659
  """ 批量修改数据库的列名 """
660
660
  """
661
- # for db_name in ['京东数据2', '天猫数据2', '市场数据2', '生意参谋数据2', '生意经2', '属性设置2',]:
661
+ # for db_name in ['京东数据2', '推广数据2', '市场数据2', '生意参谋2', '生意经2', '属性设置2',]:
662
662
  # s = OptimizeDatas(username=username, password=password, host=host, port=port)
663
663
  # s.db_name = db_name
664
664
  # s.rename_column()
@@ -715,9 +715,9 @@ if __name__ == '__main__':
715
715
 
716
716
  # for db_name in [
717
717
  # '京东数据2',
718
- # '天猫数据2',
718
+ # '推广数据2',
719
719
  # '市场数据2',
720
- # '生意参谋数据2',
720
+ # '生意参谋2',
721
721
  # '生意经2',
722
722
  # '属性设置2',
723
723
  # ]:
@@ -35,20 +35,23 @@ class MysqlUpload:
35
35
  'charset': charset, # utf8mb4 支持存储四字节的UTF-8字符集
36
36
  'cursorclass': pymysql.cursors.DictCursor,
37
37
  }
38
+ self.filename = None
38
39
 
39
- def df_to_mysql(self, df, table_name, db_name='远程数据源', drop_duplicates=False):
40
+ def df_to_mysql(self, df, table_name, db_name='远程数据源', drop_duplicates=False, filename=None, count=None):
40
41
  """
41
42
  将 df 写入数据库
42
43
  db_name: 数据库名称
43
44
  table_name: 集合/表名称
44
45
  drop_duplicates:仅限于聚合数据使用,其他情况不要设置
46
+ filename: 传这个参数是方便定位产生错误的文件
45
47
  """
48
+ self.filename = filename
46
49
  if isinstance(df, pd.DataFrame):
47
50
  if len(df) == 0:
48
- print(f'{db_name}: {table_name} 传入的 df 数据长度为0')
51
+ print(f'{db_name}: {table_name} 传入的 df 数据长度为0, {self.filename}')
49
52
  return
50
53
  else:
51
- print(f'{db_name}: {table_name} 传入的 df 不是有效的 dataframe 结构')
54
+ print(f'{db_name}: {table_name} 传入的 df 不是有效的 dataframe 结构, {self.filename}')
52
55
  return
53
56
  cv = converter.DataFrameConverter()
54
57
  df = cv.convert_df_cols(df=df) # 清理 dataframe 非法值
@@ -95,7 +98,7 @@ class MysqlUpload:
95
98
  for col in col_not_exist:
96
99
  try:
97
100
  # 创建列,需转义
98
- sql = f"ALTER TABLE `{table_name}` ADD COLUMN `{col}` {dtypes[col]} DEFAULT NULL;"
101
+ sql = f"ALTER TABLE `{table_name}` ADD COLUMN `{col}` {dtypes[col]};"
99
102
  cursor.execute(sql)
100
103
  print(f"添加列: {col}({dtypes[col]})") # 添加列并指定数据类型
101
104
 
@@ -105,8 +108,8 @@ class MysqlUpload:
105
108
  result = cursor.fetchone() # 检查索引是否存在
106
109
  if not result:
107
110
  cursor.execute(f"CREATE INDEX index_name ON `{table_name}`(`{col}`)")
108
- except:
109
- pass
111
+ except Exception as e:
112
+ print(f'{self.filename}: {e}')
110
113
  connection.commit() # 提交事务
111
114
 
112
115
  # 返回这些结果的目的是等添加完列再写 json 文件才能读到 types 信息
@@ -128,7 +131,7 @@ class MysqlUpload:
128
131
 
129
132
  # 5. 更新插入数据
130
133
  now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
131
- print(f'{now}正在更新 mysql ({self.host}:{self.port}) {db_name}/{table_name}')
134
+ print(f'{now}正在更新 mysql ({self.host}:{self.port}) {db_name}/{table_name}, {count},{self.filename}')
132
135
  datas = df.to_dict(orient='records')
133
136
  for data in datas:
134
137
  try:
@@ -142,13 +145,19 @@ class MysqlUpload:
142
145
  # print(condition)
143
146
 
144
147
  sql = f"SELECT {cols} FROM `{table_name}` WHERE {condition}"
148
+ # sql = f"SELECT {cols} FROM `{table_name}` WHERE `创建时间` = '2014-09-19 14:32:33'"
145
149
  cursor.execute(sql)
146
- result = cursor.fetchall() # 获取查询结果, 如果有结果返回 list,没有则返回空元组 tuple
150
+ result = cursor.fetchall() # 获取查询结果, 有结果返回 list 表示数据已存在(不重复插入),没有则返回空 tuple
147
151
  if not result: # 数据不存在则插入
148
152
  sql = f"INSERT INTO `{table_name}` ({cols}) VALUES ({values});"
149
153
  cursor.execute(sql)
154
+ # else:
155
+ # print(f'重复数据不插入: {condition[:50]}...')
150
156
  except Exception as e:
151
- print(f'mysql -> df_to_mysql 报错: {e}')
157
+ # print(data)
158
+ # print(values)
159
+ print(f'mysql -> df_to_mysql 报错: {e}, {self.filename}')
160
+ # breakpoint()
152
161
  connection.commit() # 提交事务
153
162
 
154
163
  def convert_dtypes(self, df, db_name, table_name):
@@ -196,32 +205,35 @@ class MysqlUpload:
196
205
  return longest_value
197
206
 
198
207
  # 最优先处理 ID 类型, 在 mysql 里面, 有些列数字过长不能存储为 int 类型
199
- if 'id' in col or 'ID' in col or 'Id' in col or '摘要' in col or '商家编码' in col or '单号' in col or '款号' in col:
200
- return 'mediumtext'
208
+ # if 'id' in col or 'ID' in col or 'Id' in col or '摘要' in col or '商家编码' in col or '单号' in col or '款号' in col:
209
+ # return 'mediumtext'
201
210
  if '商品编码' in col: # 京东sku/spu商品信息
202
211
  return 'mediumtext'
203
212
  if '文件大小' in col: # bw 程序
204
213
  return 'mediumtext'
205
- elif '日期' in col or '时间' in col:
206
- try:
207
- k = pd.to_datetime(df[col].tolist()[0]) # 检查是否可以转为日期
208
- return 'DATE'
209
- except:
210
- return 'mediumtext'
211
- elif dtype == 'datetime64[ns]': # 日期可能显示为数字, 因为放在判断 int 的前面
212
- return 'DATE'
214
+ # elif '日期' in col or '时间' in col:
215
+ # try:
216
+ # k = pd.to_datetime(df[col].tolist()[0]) # 检查是否可以转为日期
217
+ # return 'DATE'
218
+ # except:
219
+ # return 'mediumtext'
220
+ elif dtype == 'datetime64[ns]':
221
+ return 'DATETIME' # 使用 DATE 后续排重可能会引发不能排重
213
222
  elif dtype == 'int32':
214
- if len(str(max(df[col].tolist()))) >= 10: # 数值长度超限转为 mediumtext
223
+ max_num = str(max(df[col].tolist()))
224
+ if len(max_num) >= 10: # 数值长度超限转为 mediumtext
215
225
  return 'mediumtext'
216
226
  return 'INT'
217
227
  elif dtype == 'int64':
218
- if len(str(max(df[col].tolist()))) >= 10:
228
+ max_num = str(max(df[col].tolist()))
229
+ if len(max_num) >= 10:
219
230
  return 'mediumtext'
220
231
  return 'INT'
221
232
  elif dtype == 'float64':
222
233
  res = find_longest_decimal_value(df[col].tolist()) # 取小数位数最长的值
223
234
  int_step = len(str(res).split('.')[0]) # 整数位数长度
224
235
  f_step = len(str(res).split('.')[1]) # 小数位数长度
236
+
225
237
  if int_step >= 12:
226
238
  return 'mediumtext' # mysql 中不要使用 float 和 double 类型,会影响计算结果
227
239
  elif int_step >= 8 and f_step >= 0:
@@ -231,14 +243,18 @@ class MysqlUpload:
231
243
  elif int_step >= 4 and f_step >= 0:
232
244
  return 'decimal(10, 2)'
233
245
  elif int_step >= 2 and f_step >= 6:
234
- return 'decimal(12, 4)'
235
- elif int_step >= 2 and f_step >= 4:
246
+ return 'decimal(12, 6)'
247
+ elif int_step >= 2 and f_step > 4:
248
+ return 'decimal(10, 6)'
249
+ elif int_step >= 2 and f_step > 2:
236
250
  return 'decimal(10, 4)'
237
251
  elif int_step >= 2 and f_step >= 0:
238
252
  return 'decimal(10, 2)'
239
253
  elif int_step >= 1 and f_step >= 6:
240
254
  return 'decimal(10, 6)'
241
- elif int_step >= 1 and f_step >= 4:
255
+ elif int_step >= 1 and f_step > 4:
256
+ return 'decimal(10, 6)'
257
+ elif int_step >= 1 and f_step > 2:
242
258
  return 'decimal(10, 4)'
243
259
  else:
244
260
  return 'decimal(10, 2)'
@@ -619,7 +635,7 @@ class OptimizeDatas:
619
635
  def rename_column(self):
620
636
  """ 批量修改数据库的列名 """
621
637
  """
622
- # for db_name in ['京东数据2', '天猫数据2', '市场数据2', '生意参谋数据2', '生意经2', '属性设置2',]:
638
+ # for db_name in ['京东数据2', '推广数据2', '市场数据2', '生意参谋2', '生意经2', '属性设置2',]:
623
639
  # s = OptimizeDatas(username=username, password=password, host=host, port=port)
624
640
  # s.db_name = db_name
625
641
  # s.rename_column()
@@ -691,22 +707,22 @@ def download_datas(table_name, save_path, start_date):
691
707
 
692
708
  def one_file_to_mysql(file, db_name, table_name, target_service, database):
693
709
  username, password, host, port = get_myconf.select_config_values(target_service=target_service, database=database)
694
-
695
- df = pd.read_csv(file, encoding='utf-8_sig', header=0, na_filter=False)
710
+ filename = os.path.basename(file)
711
+ df = pd.read_csv(file, encoding='utf-8_sig', header=0, na_filter=False, float_precision='high')
696
712
  m = MysqlUpload(username=username, password=password, host=host, port=port)
697
- m.df_to_mysql(df=df, db_name=db_name, table_name=table_name)
713
+ m.df_to_mysql(df=df, db_name=db_name, table_name=table_name, filename=filename)
698
714
 
699
715
 
700
716
  if __name__ == '__main__':
701
- username, password, host, port = get_myconf.select_config_values(target_service='company', database='mysql')
702
- print(username, password, host, port)
717
+ # username, password, host, port = get_myconf.select_config_values(target_service='home_lx', database='mysql')
718
+ # print(username, password, host, port)
703
719
 
704
- file = '/Users/xigua/数据中心/原始文件2/京东报表/JD商品明细spu/2024-08/spu_2024-08-18_全部渠道_商品明细.csv'
720
+ file = '/Users/xigua/数据中心/原始文件2/推广报表/创意报表/创意报表_万里马官方旗舰店_2024-07-05_2024-07-19.csv'
705
721
  one_file_to_mysql(
706
722
  file=file,
707
- db_name='test',
708
- table_name='余额查询',
709
- target_service='company',
723
+ db_name='推广数据2',
724
+ table_name='创意报表',
725
+ target_service='home_lx',
710
726
  database='mysql'
711
727
  )
712
728
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mdbq
3
- Version: 1.1.1
3
+ Version: 1.1.3
4
4
  Home-page: https://pypi.org/project/mdbsql
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com
@@ -3,7 +3,7 @@
3
3
  from setuptools import setup, find_packages
4
4
 
5
5
  setup(name='mdbq',
6
- version='1.1.1',
6
+ version='1.1.3',
7
7
  author='xigua, ',
8
8
  author_email="2587125111@qq.com",
9
9
  url='https://pypi.org/project/mdbsql',
@@ -1,112 +0,0 @@
1
- # -*- coding:utf-8 -*-
2
- import pandas as pd
3
- import numpy as np
4
- import re
5
-
6
-
7
- class DataFrameConverter(object):
8
- def __init__(self, df=pd.DataFrame({})):
9
- self.df = df
10
-
11
- def convert_df_cols(self, df=pd.DataFrame({})):
12
- """
13
- 清理 dataframe 非法值
14
- 对数据类型进行转换(尝试将 object 类型转为 int 或 float)
15
- """
16
- if len(df) == 0:
17
- df = self.df
18
- if len(df) == 0:
19
- return
20
- # dtypes = df.dtypes.apply(str).to_dict() # 将 dataframe 数据类型转为字典形式
21
- df.replace([np.inf, -np.inf], 0, inplace=True) # 清理一些非法值
22
- df.replace(to_replace=['\\N', '-', '--', '', 'nan'], value=0, regex=False, inplace=True) # 替换掉特殊字符
23
- df.replace(to_replace=[','], value='', regex=True, inplace=True)
24
- df.replace(to_replace=['="'], value='', regex=True, inplace=True) # ="和"不可以放在一起清洗, 因为有: id=86785565
25
- df.replace(to_replace=['"'], value='', regex=True, inplace=True)
26
- cols = df.columns.tolist()
27
-
28
- for col in cols:
29
- # df[col] = df[col].apply(lambda x: re.sub('[="]', '', str(x)) if '="' in str(x) else x)
30
- # 百分比在某些数据库中不兼容, 转换百分比为小数
31
- df[col] = df[col].apply(lambda x: float(float((str(x).rstrip("%"))) / 100) if str(x).endswith('%') and '~' not in str(x) else x)
32
- # 尝试转换合适的数据类型
33
- if df[col].dtype == 'object':
34
- try:
35
- # df[col] = df[col].astype(int) # 尝试转换 int
36
- df[col] = df[col].apply(lambda x: int(x) if '_' not in str(x) else x)
37
- except:
38
- # df[col] = df[col].astype('float64', errors='ignore') # 尝试转换 float, 报错则忽略
39
- try:
40
- df[col] = df[col].apply(lambda x: float(x) if '_' not in str(x) else x)
41
- except:
42
- pass
43
- if df[col].dtype == 'float': # 对于小数类型, 保留 6 位小数
44
- df[col] = df[col].apply(lambda x: round(float(x), 6) if x != 0 else x)
45
- # 清理列名, 在 mysql 里面列名不能含有某些特殊字符
46
- if '日期' in col or '时间' in col:
47
- try:
48
- df[col] = df[col].apply(lambda x: pd.to_datetime(x))
49
- except:
50
- pass
51
- new_col = col.lower()
52
- df.rename(columns={col: new_col}, inplace=True)
53
- df.fillna(0, inplace=True)
54
- return df
55
-
56
- def convert_df_cols_bak(self, df=pd.DataFrame({})):
57
- """
58
- 清理 dataframe 列名的不合规字符(mysql)
59
- 对数据类型进行转换(尝试将 object 类型转为 int 或 float)
60
- """
61
- if len(df) == 0:
62
- df = self.df
63
- if len(df) == 0:
64
- return
65
- # dtypes = df.dtypes.apply(str).to_dict() # 将 dataframe 数据类型转为字典形式
66
- df.replace([np.inf, -np.inf], 0, inplace=True) # 清理一些非法值
67
- df.replace(to_replace=['\\N', '-', '--', '', 'nan'], value=0, regex=False, inplace=True) # 替换掉特殊字符
68
- df.replace(to_replace=[','], value='', regex=True, inplace=True)
69
- df.replace(to_replace=['="'], value='', regex=True, inplace=True) # ="和"不可以放在一起清洗, 因为有: id=86785565
70
- df.replace(to_replace=['"'], value='', regex=True, inplace=True)
71
- cols = df.columns.tolist()
72
-
73
- for col in cols:
74
- # df[col] = df[col].apply(lambda x: re.sub('[="]', '', str(x)) if '="' in str(x) else x)
75
- # 百分比在某些数据库中不兼容, 转换百分比为小数
76
- df[col] = df[col].apply(lambda x: float(float((str(x).rstrip("%"))) / 100) if str(x).endswith('%') and '~' not in str(x) else x)
77
- # 尝试转换合适的数据类型
78
- if df[col].dtype == 'object':
79
- try:
80
- # df[col] = df[col].astype(int) # 尝试转换 int
81
- df[col] = df[col].apply(lambda x: int(x) if '_' not in str(x) else x)
82
- except:
83
- # df[col] = df[col].astype('float64', errors='ignore') # 尝试转换 float, 报错则忽略
84
- try:
85
- df[col] = df[col].apply(lambda x: float(x) if '_' not in str(x) else x)
86
- except:
87
- pass
88
- if df[col].dtype == 'float': # 对于小数类型, 保留 6 位小数
89
- df[col] = df[col].apply(lambda x: round(float(x), 6) if x != 0 else x)
90
- # 清理列名, 在 mysql 里面列名不能含有某些特殊字符
91
- if '日期' in col or '时间' in col:
92
- try:
93
- df[col] = df[col].apply(lambda x: pd.to_datetime(x))
94
- except:
95
- pass
96
- new_col = col.lower()
97
- new_col = re.sub(r'[\',,()()/=<>+\-*^"’\[\]~#|&% .;]', '_', new_col)
98
- new_col = re.sub(r'_+$', '', new_col)
99
- df.rename(columns={col: new_col}, inplace=True)
100
- df.fillna(0, inplace=True)
101
- return df
102
-
103
-
104
- if __name__ == '__main__':
105
- # df = pd.DataFrame(np.random.randn(5, 3), columns=['a', 'b', 'c'])
106
- # converter = DataFrameConverter()
107
- # df = converter.convert_df_cols(df)
108
- # print(df['a'].dtype)
109
- # print(df)
110
- pattern = 'dfa_dfawr__'
111
- pattern = re.sub(r'_+$', '', pattern)
112
- print(pattern)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes