mdbq 3.3.15__py3-none-any.whl → 3.3.16__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.
@@ -1,5 +1,6 @@
1
1
  # -*- coding: UTF-8 –*-
2
2
  import os
3
+ import re
3
4
  import socket
4
5
  import platform
5
6
  import datetime
@@ -444,7 +445,7 @@ class DataShow:
444
445
  yref='paper',
445
446
  showarrow=False, # 显示箭头
446
447
  align="left", # 文本对齐方式
447
- font=dict(size=16),
448
+ font=dict(size=14),
448
449
  )
449
450
  fig.update_layout(
450
451
  title_text=f'多店推广花费_按日聚合',
@@ -499,12 +500,11 @@ class DataShow:
499
500
  print(f'{table_name}: 数据长度不能为 0')
500
501
  continue
501
502
  df['日期'] = pd.to_datetime(df['日期'])
503
+
502
504
  df['商品id'] = df['商品id'].astype('int64')
503
505
  df = df[df['商品id'] == int(item_id)]
504
- last_date = df['日期'].max()
505
506
  # 对数据进行筛选
506
507
  df = df[
507
- (df['日期'] == last_date) &
508
508
  ~df['标签名称'].str.contains('unknown', case=False) &
509
509
  (df['洞察类型'] == lab) &
510
510
  (df['行为类型'] == option) &
@@ -515,6 +515,7 @@ class DataShow:
515
515
  fig = make_subplots(rows=2, cols=3)
516
516
  # 在每个子图中绘制柱形图
517
517
  count = 0
518
+ sv_date = {}
518
519
  for table_name, df in dict_list.items():
519
520
  if len(df) == 0:
520
521
  count += 1
@@ -522,7 +523,12 @@ class DataShow:
522
523
  # print(count, table_name)
523
524
  if count > 5:
524
525
  break
525
- labels = df['标签名称'].tolist()
526
+ last_date = df['日期'].max()
527
+ sv_date.update({table_name: last_date.strftime('%Y-%m-%d')})
528
+ df = df[df['日期'] == last_date]
529
+ # 先进行排序,以便柱形图从高到底
530
+ df.sort_values(['标签人群数量'], ascending=[False], ignore_index=True, inplace=True)
531
+ labels = df['标签名称'].tolist() # 由于上面有自定义排序,labels 和 values 要放在一起
526
532
  values = df['标签人群数量'].tolist()
527
533
  df['Percentage'] = df['标签人群数量'] / df['标签人群数量'].sum() * 100
528
534
  percentages = df['Percentage']
@@ -530,8 +536,10 @@ class DataShow:
530
536
  x=labels,
531
537
  y=values,
532
538
  name=table_name,
539
+ orientation='v', # 垂直柱形图
533
540
  text=percentages.map('{:.2f}%'.format), # 设置要显示的文本(百分比)
534
541
  # textposition = 'outside', # 设置文本位置在柱形图外部
542
+ width=0.55 # 调整柱子最大宽度
535
543
  )
536
544
  row = count // 3 + 1
537
545
  col = count % 3 + 1
@@ -541,20 +549,20 @@ class DataShow:
541
549
  col=col,
542
550
  )
543
551
  if count < 3:
544
- x = 0.01 + 0.395 * (count)
552
+ x = 0.01 + 0.385 * (count)
545
553
  y = 1.04
546
554
  else:
547
- x = 0.01 + 0.395 * (count % 3)
555
+ x = 0.01 + 0.385 * (count % 3)
548
556
  y = 1.04 - 0.59 * (count // 3)
549
557
  fig.add_annotation(
550
- text=table_name,
558
+ text=f'{table_name}',
551
559
  x=x,
552
560
  y=y,
553
561
  xref='paper', # # 相对于整个图表区域
554
562
  yref='paper',
555
563
  showarrow=False, # 显示箭头
556
564
  align="left", # 文本对齐方式
557
- font=dict(size=16),
565
+ font=dict(size=15),
558
566
  )
559
567
  count += 1
560
568
 
@@ -573,7 +581,7 @@ class DataShow:
573
581
  # legend=dict(orientation="h")
574
582
  )
575
583
  fig.add_annotation(
576
- text=f'数据日期: {last_date.strftime('%Y-%m-%d')} 统计范围: {lab}/{option} {d_str}',
584
+ text=f'统计范围: {lab}/{option} {d_str}',
577
585
  x=0.5,
578
586
  y=-0.1,
579
587
  xref='paper', # # 相对于整个图表区域
@@ -582,17 +590,166 @@ class DataShow:
582
590
  align="left", # 文本对齐方式
583
591
  font=dict(size=14),
584
592
  )
593
+ fig.add_annotation(
594
+ text=re.sub('[{}\',]', '', str(sv_date)),
595
+ x=0.5,
596
+ y=-0.135,
597
+ xref='paper', # # 相对于整个图表区域
598
+ yref='paper',
599
+ showarrow=False, # 显示箭头
600
+ align="left", # 文本对齐方式
601
+ font=dict(size=12),
602
+ )
603
+ fig.write_html(os.path.join(self.path, f'{filename}.html'))
604
+
605
+ def crowd(self, db_name='人群画像2', table_list=None, pro_list=None, filename='达摩盘人群画像', crowd_id=None, last_date=None):
606
+ # item_ids = [696017020186, 714066010148, 830890472575]
607
+ if not pro_list:
608
+ pro_list = ['日期', '店铺名称', '人群id', '人群名称', '标签名称', '标签人群数量']
609
+ if not table_list:
610
+ table_list = [
611
+ '消费能力等级',
612
+ '用户年龄',
613
+ '月均消费金额',
614
+ '大快消策略人群',
615
+ '店铺潜新老客',
616
+ '城市等级',
617
+ '用户职业',
618
+ ]
619
+ if not crowd_id:
620
+ crowd_id = 40457369
621
+
622
+ dict_list = {}
623
+ for table_name in table_list:
624
+ df = self.getdata(db_name=db_name, table_name=table_name, pro_list=pro_list)
625
+ if len(df) == 0:
626
+ print(f'{table_name}: 数据长度不能为 0')
627
+ continue
628
+ df['日期'] = pd.to_datetime(df['日期'])
629
+
630
+ df['人群id'] = df['人群id'].astype('int64')
631
+ df = df[df['人群id'] == int(crowd_id)]
632
+ # 对数据进行筛选
633
+ df = df[
634
+ (df['店铺名称'] == '万里马官方旗舰店')
635
+ # ~df['标签名称'].str.contains('unknown', case=False)
636
+ ]
637
+ dict_list.update({table_name: df})
638
+ crowd_name = df.head(1)['人群名称'].tolist()[0] # 随便取一条数据读取人群名称
639
+ fig = make_subplots(rows=2, cols=3)
640
+ # 在每个子图中绘制柱形图
641
+ count = 0
642
+ sv_date = {}
643
+ unknown_dict = {}
644
+ for table_name, df in dict_list.items():
645
+ if len(df) == 0:
646
+ count += 1
647
+ continue
648
+ # print(count, table_name)
649
+ if count > 5:
650
+ break
651
+ last_date = df['日期'].max()
652
+ df = df[df['日期'] == last_date]
653
+ unknown = df[df['标签名称'].str.contains('unknown', case=False)]
654
+ if len(unknown) > 0:
655
+ unknown = unknown['标签人群数量'].tolist()[0] # 未知人群数量值
656
+
657
+ df = df[~df['标签名称'].str.contains('unknown', case=False)]
658
+ # 先进行排序,以便柱形图从高到底
659
+ df.sort_values(['标签人群数量'], ascending=[False], ignore_index=True, inplace=True)
660
+ labels = df['标签名称'].tolist() # 由于上面有自定义排序,labels 和 values 要放在一起
661
+ values = df['标签人群数量'].tolist()
662
+ crowd_sum = df['标签人群数量'].values.sum()
663
+ sv_date.update({table_name: crowd_sum})
664
+ unknown_dict.update({table_name: unknown})
665
+ df['Percentage'] = df['标签人群数量'] / df['标签人群数量'].sum() * 100
666
+ percentages = df['Percentage']
667
+ bar = go.Bar(
668
+ x=labels,
669
+ y=values,
670
+ name=table_name,
671
+ orientation='v', # 垂直柱形图
672
+ text=percentages.map('{:.2f}%'.format), # 设置要显示的文本(百分比)
673
+ # textposition = 'outside', # 设置文本位置在柱形图外部
674
+ width=0.55 # 调整柱子最大宽度
675
+ )
676
+ row = count // 3 + 1
677
+ col = count % 3 + 1
678
+ fig.add_trace(
679
+ bar,
680
+ row=row,
681
+ col=col,
682
+ )
683
+ if count < 3:
684
+ x = 0.01 + 0.42 * (count)
685
+ y = 1.04
686
+ else:
687
+ x = 0.01 + 0.42 * (count % 3)
688
+ y = 1.04 - 0.59 * (count // 3)
689
+ fig.add_annotation(
690
+ text=f'{table_name} 人群数量: {crowd_sum}',
691
+ x=x,
692
+ y=y,
693
+ xref='paper', # # 相对于整个图表区域
694
+ yref='paper',
695
+ showarrow=False, # 显示箭头
696
+ align="left", # 文本对齐方式
697
+ font=dict(size=15),
698
+ )
699
+ count += 1
700
+
701
+ fig.update_layout(
702
+ title_text=f'达摩盘人群画像 人群id: {crowd_id} / 人群名字: 【{crowd_name}】',
703
+ xaxis_title='标签',
704
+ yaxis_title='人群数量',
705
+ # width=self.screen_width // 1.4,
706
+ # height=self.screen_width // 2,
707
+ margin=dict(
708
+ l=100, # 左边距
709
+ r=100,
710
+ t=100, # 上边距
711
+ b=100,
712
+ ),
713
+ # legend=dict(orientation="h")
714
+ )
715
+ res = {}
716
+ for k, v in sv_date.items():
717
+ res.update({k: int(v)})
718
+ unknown_res = {}
719
+ for k, v in unknown_dict.items():
720
+ unknown_res.update({k: int(v)})
721
+
722
+ fig.add_annotation(
723
+ text=f'分析人群数量: {re.sub('[{}\',]', '', str(res))}',
724
+ x=0.5,
725
+ y=-0.1,
726
+ xref='paper', # # 相对于整个图表区域
727
+ yref='paper',
728
+ showarrow=False, # 显示箭头
729
+ align="left", # 文本对齐方式
730
+ font=dict(size=12),
731
+ )
732
+ fig.add_annotation(
733
+ text=f'与官方统计存在差异,官方计算中包含未知人群,数量为: {re.sub('[{}\',]', '', str(unknown_res))},未知人群占比越大,同官方差异越大',
734
+ x=0.5,
735
+ y=-0.135,
736
+ xref='paper', # # 相对于整个图表区域
737
+ yref='paper',
738
+ showarrow=False, # 显示箭头
739
+ align="left", # 文本对齐方式
740
+ font=dict(size=12),
741
+ )
585
742
  fig.write_html(os.path.join(self.path, f'{filename}.html'))
586
743
 
587
744
 
588
745
  def main():
589
746
  ds = DataShow()
590
- # ds.dpll()
591
- # ds.tg(
592
- # days=15,
593
- # # start_date='2024-11-01',
594
- # # end_date='2024-11-30',
595
- # )
747
+ ds.dpll()
748
+ ds.tg(
749
+ days=15,
750
+ # start_date='2024-11-01',
751
+ # end_date='2024-11-30',
752
+ )
596
753
  ds.item_crowd(
597
754
  item_id=839148235697,
598
755
  lab='全部渠道',
@@ -600,6 +757,10 @@ def main():
600
757
  last_date=None,
601
758
  d_str='近30天',
602
759
  )
760
+ ds.crowd(
761
+ crowd_id=40457166,
762
+ last_date=None,
763
+ )
603
764
 
604
765
 
605
766
  if __name__ == '__main__':
@@ -482,19 +482,37 @@ class MysqlDatasQuery:
482
482
  }
483
483
  )
484
484
  df.insert(loc=1, column='推广渠道', value='万相台无界版') # df中插入新列
485
+
486
+ # 开始处理用户特征
487
+ df_sx = self.download.data_to_df(
488
+ db_name='达摩盘3',
489
+ table_name=f'我的人群属性',
490
+ start_date=start_date,
491
+ end_date=end_date,
492
+ projection={'人群名称': 1, '消费能力等级': 1, '用户年龄': 1},
493
+ )
494
+ df_sx['人群名称'] = df_sx['人群名称'].apply(lambda x: f'达摩盘:{x}')
495
+ df_sx.rename(columns={'消费能力等级': '消费力层级'}, inplace=True)
496
+ df = pd.merge(df, df_sx, left_on=['人群名字'], right_on=['人群名称'], how='left')
497
+ df.pop('人群名称')
498
+ df['消费力层级'] = df['消费力层级'].apply(lambda x: f'L{"".join(re.findall(r'L(\d)', str(x)))}' if str(x) != 'nan' else x)
499
+ df['用户年龄'] = df['用户年龄'].apply(lambda x: "~".join(re.findall(r'(\d{2})\D.*(\d{2})岁', str(x))[0]) if str(x) != 'nan' else x)
500
+
485
501
  # 1. 匹配 L后面接 2 个或以上数字,不区分大小写,示例:L345
486
502
  # 2. 其余情况,L 后面接多个数字的都会被第一条 if 命中,不区分大小写
503
+
487
504
  df['消费力层级'] = df.apply(
488
505
  lambda x:
489
506
  ''.join(re.findall(r'(l\d+)', x['人群名字'].upper(), re.IGNORECASE)) if re.findall(r'(l\d{2,})',
490
507
  x['人群名字'],
491
- re.IGNORECASE)
492
- else 'L5' if re.findall(r'(l\d*5)', x['人群名字'], re.IGNORECASE)
493
- else 'L4' if re.findall(r'(l\d*4)', x['人群名字'], re.IGNORECASE)
494
- else 'L3' if re.findall(r'(l\d*3)', x['人群名字'], re.IGNORECASE)
495
- else 'L2' if re.findall(r'(l\d*2)', x['人群名字'], re.IGNORECASE)
496
- else 'L1' if re.findall(r'(l\d*1)', x['人群名字'], re.IGNORECASE)
497
- else '', axis=1)
508
+ re.IGNORECASE) and str(x['消费力层级']) == 'nan'
509
+ else 'L5' if re.findall(r'(l\d*5)', x['人群名字'], re.IGNORECASE) and str(x['消费力层级']) == 'nan'
510
+ else 'L4' if re.findall(r'(l\d*4)', x['人群名字'], re.IGNORECASE) and str(x['消费力层级']) == 'nan'
511
+ else 'L3' if re.findall(r'(l\d*3)', x['人群名字'], re.IGNORECASE) and str(x['消费力层级']) == 'nan'
512
+ else 'L2' if re.findall(r'(l\d*2)', x['人群名字'], re.IGNORECASE) and str(x['消费力层级']) == 'nan'
513
+ else 'L1' if re.findall(r'(l\d*1)', x['人群名字'], re.IGNORECASE) and str(x['消费力层级']) == 'nan'
514
+ else x['消费力层级'], axis=1)
515
+
498
516
  # 1. 匹配连续的 4 个数字且后面不能接数字或"元"或汉字,筛掉的人群示例:月均消费6000元|受众20240729175213|xxx2024真皮公文包
499
517
  # 2. 匹配 2数字_2数字且前面不能是数字,合法匹配:人群_30_50_促; 非法示例:L345_3040 避免识别出 35~20 岁用户的情况
500
518
  # pattern = r'(\d{4})(?!\d|[\u4e00-\u9fa5])' # 匹配 4 个数字,后面不能接数字或汉字
@@ -506,21 +524,29 @@ class MysqlDatasQuery:
506
524
  pattern2 = r'(?<![\dlL])(\d{2}_\d{2})'
507
525
  df['用户年龄'] = df.apply(
508
526
  lambda x:
509
- ''.join(re.findall(pattern1, x['人群名字'].upper())) if re.findall(pattern1, x['人群名字'])
527
+ ''.join(re.findall(pattern1, x['人群名字'].upper())) if re.findall(pattern1, x['人群名字']) and str(x['用户年龄']) == 'nan'
510
528
  # else ''.join(re.findall(r'[^\d|l|L](\d{2}_\d{2})', x['人群名字'].upper())) if re.findall(r'[^\d|l|L](\d{2}_\d{2})', x['人群名字'])
511
- else ''.join(re.findall(pattern2, x['人群名字'].upper())) if re.findall(pattern2, x['人群名字'])
529
+ else ''.join(re.findall(pattern2, x['人群名字'].upper())) if re.findall(pattern2, x['人群名字']) and str(x['用户年龄']) == 'nan'
512
530
  else ''.join(re.findall(r'(\d{2}-\d{2})岁', x['人群名字'].upper())) if re.findall(r'(\d{2}-\d{2})岁',
513
- x['人群名字'])
514
- else '', axis=1)
531
+ x['人群名字']) and str(x['用户年龄']) == 'nan'
532
+ else x['用户年龄'], axis=1)
515
533
  df['用户年龄'] = df['用户年龄'].apply(
516
534
  lambda x: f'{x[:2]}~{x[2:4]}' if str(x).isdigit()
517
- else str(x).replace('_', '~') if '_' in x
518
- else str(x).replace('-', '~') if '-' in x
535
+ else str(x).replace('_', '~') if '_' in str(x)
536
+ else str(x).replace('-', '~') if '-' in str(x)
519
537
  else x
520
538
  )
521
539
  # 年龄层不能是 0 开头
522
540
  df['用户年龄'] = df['用户年龄'].apply(
523
541
  lambda x: '' if str(x).startswith('0') else x)
542
+ df['用户年龄'] = df['用户年龄'].apply(
543
+ lambda x:
544
+ re.sub(f'~50', '~49' ,str(x)) if '~50' in str(x) else
545
+ re.sub(f'~40', '~39', str(x)) if '~40' in str(x) else
546
+ re.sub(f'~30', '~29' ,str(x)) if '~30' in str(x) else
547
+ re.sub(r'\d{4}~', '', str(x)) if str(x) != 'nan' else
548
+ x
549
+ )
524
550
  # df = df.head(1000)
525
551
  # df.to_csv('/Users/xigua/Downloads/test.csv', index=False, header=True, encoding='utf-8_sig')
526
552
  # breakpoint()
@@ -3809,6 +3835,10 @@ if __name__ == '__main__':
3809
3835
  # query3(months=2, less_dict=[])
3810
3836
 
3811
3837
  sdq = MysqlDatasQuery() # 实例化数据处理类
3812
- sdq.months = 100 # 设置数据周期, 1 表示近 2 个月
3838
+ sdq.months = 1 # 设置数据周期, 1 表示近 2 个月
3813
3839
  sdq.update_service = True # 调试时加,true: 将数据写入 mysql 服务器
3814
- sdq.dplyd(db_name='聚合数据', table_name='店铺流量来源构成')
3840
+ sdq.tg_rqbb(db_name='聚合数据', table_name='天猫_人群报表')
3841
+
3842
+ # string = '30-34岁,35-39岁,40-49岁'
3843
+ # d = "~".join(re.findall(r'(\d+)\D.*\D(\d+)岁', string)[0])
3844
+ # print(d)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mdbq
3
- Version: 3.3.15
3
+ Version: 3.3.16
4
4
  Home-page: https://pypi.org/project/mdbq
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com
@@ -2,9 +2,9 @@ mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
2
2
  mdbq/__version__.py,sha256=y9Mp_8x0BCZSHsdLT_q5tX9wZwd5QgqrSIENLrb6vXA,62
3
3
  mdbq/aggregation/__init__.py,sha256=EeDqX2Aml6SPx8363J-v1lz0EcZtgwIBYyCJV6CcEDU,40
4
4
  mdbq/aggregation/aggregation.py,sha256=-yzApnlqSN2L0E1YMu5ml-W827qpKQvWPCOI7jj2kzY,80264
5
- mdbq/aggregation/datashow.py,sha256=sNZYwhPraF3KmcYdBVKvVaVoN-jGjh7ALRRLpanzQ6w,25673
5
+ mdbq/aggregation/datashow.py,sha256=2NzHGjGoUy2WG-MxmbilCj6KBAmVah3jqFuEd2zv9XU,32379
6
6
  mdbq/aggregation/optimize_data.py,sha256=RXIv7cACCgYyehAxMjUYi_S7rVyjIwXKWMaM3nduGtA,3068
7
- mdbq/aggregation/query_data.py,sha256=4Fd4dMGi6Cu-KgNTf1OBNYe8InjvpMA5JALxCwvsHyw,173841
7
+ mdbq/aggregation/query_data.py,sha256=FcwaYUom2UGqCRsuGgwfuVdnY86PUOzkCivyoCY2oVQ,175663
8
8
  mdbq/bdup/__init__.py,sha256=AkhsGk81SkG1c8FqDH5tRq-8MZmFobVbN60DTyukYTY,28
9
9
  mdbq/bdup/bdup.py,sha256=LAV0TgnQpc-LB-YuJthxb0U42_VkPidzQzAagan46lU,4234
10
10
  mdbq/config/__init__.py,sha256=jso1oHcy6cJEfa7udS_9uO5X6kZLoPBF8l3wCYmr5dM,18
@@ -34,7 +34,7 @@ mdbq/pbix/refresh_all.py,sha256=OBT9EewSZ0aRS9vL_FflVn74d4l2G00wzHiikCC4TC0,5926
34
34
  mdbq/pbix/refresh_all_old.py,sha256=_pq3WSQ728GPtEG5pfsZI2uTJhU8D6ra-htIk1JXYzw,7192
35
35
  mdbq/spider/__init__.py,sha256=RBMFXGy_jd1HXZhngB2T2XTvJqki8P_Fr-pBcwijnew,18
36
36
  mdbq/spider/aikucun.py,sha256=eAIITxnbbxsR_EoohJ78CRw2dEdfSHOltfpxBrh0cvc,22207
37
- mdbq-3.3.15.dist-info/METADATA,sha256=ZoivLw_LLapTkSRtAZGP2xvD8jpaBpX53MrIGKJ_LkQ,244
38
- mdbq-3.3.15.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
39
- mdbq-3.3.15.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
40
- mdbq-3.3.15.dist-info/RECORD,,
37
+ mdbq-3.3.16.dist-info/METADATA,sha256=c2t76yzpaP9kkwDg5y3Ooam9oYe6p4ntlKjWFUjZ464,244
38
+ mdbq-3.3.16.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
39
+ mdbq-3.3.16.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
40
+ mdbq-3.3.16.dist-info/RECORD,,
File without changes