forked from XYZliang/CAAI-BDSC2023-Task2-rank2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Program.cs
1748 lines (1543 loc) · 110 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Text.Json;
using LitJson;
using System.Diagnostics;
using ShellProgressBar;
// 模拟python tqdm进度条
public static class ProgressBarExtensions
{
public static IEnumerable<T> WithProgressBar<T>(this IEnumerable<T> enumerable, string message = "Processing...")
{
var totalCount = enumerable.Count();
var processedItems = 0;
var stopwatch = Stopwatch.StartNew();
var progressBar = new ShellProgressBar.ProgressBar(totalCount, message, new ProgressBarOptions
{
ProgressCharacter = '─',
ProgressBarOnBottom = true
});
foreach (var item in enumerable)
{
yield return item;
processedItems++;
progressBar.Tick();
if (processedItems > 0)
{
var elapsed = stopwatch.Elapsed;
var estimatedTotalTime = TimeSpan.FromSeconds(elapsed.TotalSeconds / processedItems * totalCount);
var estimatedTimeRemaining = estimatedTotalTime - elapsed;
progressBar.Message =
$"{message} ({processedItems}/{totalCount}) Estimated time remaining: {estimatedTimeRemaining}";
}
}
stopwatch.Stop();
progressBar.Message = $"{message}已完成,总用时:{stopwatch.Elapsed}";
progressBar.Dispose();
}
// 读取文件版本
public static IEnumerable<JsonData> LoadFilesWithProgressBar(string[] filePaths,
string message = "Loading files...")
{
var fileDataList = new List<JsonData>();
foreach (var filePath in filePaths.WithProgressBar(message))
{
var fileData = JsonMapper.ToObject(System.IO.File.ReadAllText(filePath));
fileDataList.Add(fileData);
yield return fileData;
}
}
}
public class LinkInfo
{
public string UserID { get; set; }
public string ItemID { get; set; }
public string VoterID { get; set; }
// 其他字段...
}
public class UserIDInfo
{
// 设为double形是为了后面更好的取平均值
public int Level { get; set; }
public int Gender { get; set; }
public int Age { get; set; }
// 分享次数
public int ShareNum { get; set; }
// 被分享次数
public int BeShareNum { get; set; }
// 临时储存一阶下游
public List<string> tempNeighborList { get; set; }
public List<string> tempItemList { get; set; }
public int OneNeighborNum { get; set; }
public int OneShareNum { get; set; }
public int itemNum { get; set; }
public bool bigV { get; set; }
// 该用户分享过的用户
public Dictionary<string, List<DateTime>> Neighbor { get; set; }
public Dictionary<string, List<DateTime>> NewNeighbor { get; set; }
public Dictionary<int, Dictionary<string, double>> Ratio { get; set; }
public List<DateTime> ResponesTime { get; set; }
public HashSet<string> ItemID { get; set; }
public Dictionary<int, double> responseTimeZone { get; set; }
public Dictionary<string, double> StaticSimUsers { get; set; }
public Dictionary<string, double> SimLusers;
public Dictionary<string, double> SimLLusers { get; set; }
public Dictionary<string, double> SimFFusers { get; set; }
public Dictionary<string, int> ItemPath { get; set; }
public Dictionary<string, double> SimFusers { get; set; }
public Dictionary<int, HashSet<string>> ItemPart { get; set; }
}
public class SubmitInfo
{
public string triple_id { get; set; }
public string[] candidate_voter_list { get; set; }
// 其他字段...
}
public class ItemInfo
{
public string ShopId { get; set; }
public string BrandId { get; set; }
public string CateId { get; set; }
public string CateLevelOneId { get; set; }
// 其他字段...
}
namespace tianchi
{
internal class Program : Form
{
[STAThread]
private static void Main(string[] args)
{
var program = new Program();
program.NewSumbit();
}
private void NewSumbit()
{
var ver = false;
var shareDataPath = "";
var testDataPath = "";
var itemDataPath = @"./data/item_info.json"; //定义商品信息数据文件的路径
var userDataPath = @"./data/user_info.json"; //定义用户信息数据文件的路径
if (ver)
{
shareDataPath = @"./data/item_share_train_info.json"; //定义商品分享数据文件的路径
testDataPath = @"./data/item_share_preliminary_test_info.json"; //定义测试数据文件的路径
}
else
{
shareDataPath = @"./data/item_share_train_info_AB.json"; //定义商品分享数据文件的路径
testDataPath = @"./data/item_share_test_info_B.json"; //定义测试数据文件的路径
}
// var jsonData = JsonMapper.ToObject(System.IO.File.ReadAllText(shareDataPath)); //读取并解析商品分享数据文件
// var itemjsonData = JsonMapper.ToObject(System.IO.File.ReadAllText(itemDataPath)); //读取并解析商品信息数据文件
// var userjsonData = JsonMapper.ToObject(System.IO.File.ReadAllText(userDataPath)); //读取并解析用户信息数据文件
// var testjsonData = JsonMapper.ToObject(System.IO.File.ReadAllText(testDataPath)); //读取并解析测试数据文件
// 读取文件并显示进度条
var filePaths = new[] { shareDataPath, itemDataPath, userDataPath, testDataPath };
var jsonDataList = ProgressBarExtensions.LoadFilesWithProgressBar(filePaths).ToList();
var (jsonData, itemjsonData, userjsonData, testjsonData) =
(jsonDataList[0], jsonDataList[1], jsonDataList[2], jsonDataList[3]);
var data = new SortedDictionary<DateTime, List<LinkInfo>>(); //初始化一个根据日期排序的商品分享链路信息字典
DateTime dt; //定义一个日期时间变量dt
LinkInfo lk; //定义一个链路信息变量lk
int n; //定义一个整型变量n
string itemid; //定义一个字符串变量itemid,表示商品id
string id1, id2; //定义两个字符串变量id1和id2
var users = new Dictionary<string, UserIDInfo>(); //初始化一个用户信息字典,Key为用户id,Value为用户信息
var itemsinfo = new Dictionary<string, ItemInfo>(); //初始化一个商品信息字典,Key为商品id,Value为商品信息
var itemreceive = new Dictionary<string, Dictionary<string, HashSet<string>>>(); //初始化一个嵌套字典结构,记录商品被接收的信息
var netrelation =
new Dictionary<string,
Dictionary<string, Dictionary<int, Dictionary<string, double>>>>(); //初始化一个复杂嵌套字典结构,记录网络关系信息
var responseitems = new Dictionary<string, Dictionary<string, HashSet<string>>>(); //初始化一个嵌套字典结构,记录用户响应商品的信息
var sharerank = new Dictionary<string, Dictionary<string, double>>(); //初始化一个嵌套字典结构,记录商品分享的排名信息
string item; //定义一个字符串变量item,表示商品
var ranks =
new Dictionary<string,
Dictionary<string, SortedDictionary<DateTime, List<string>>>>(); //初始化一个嵌套字典结构,记录商品的排名信息
Dictionary<string, HashSet<string>> sharenum = new Dictionary<string, HashSet<string>>(),
responseall = new Dictionary<string, HashSet<string>>(); //初始化两个字典,分别记录商品分享次数和所有的响应信息
var responsenum = new Dictionary<string, Dictionary<string, HashSet<string>>>(); //初始化一个嵌套字典结构,记录用户响应次数信息
var activenum = new Dictionary<string, double>(); //初始化一个字典,记录用户活跃数信息
var allNeigbors = new Dictionary<string, Dictionary<string, HashSet<string>>>(); //初始化一个嵌套字典结构,记录所有邻居信息
var sharetimes = new Dictionary<string, int>(); //初始化一个字典,记录商品分享次数信息
var activefrenq = new Dictionary<string, double>(); //初始化一个字典,记录活跃频率信息
var userresptime = new Dictionary<string, List<DateTime>>(); //初始化一个字典,记录用户响应时间信息
Dictionary<string, Dictionary<string, SortedSet<DateTime>>> shareusers =
new Dictionary<string, Dictionary<string, SortedSet<DateTime>>>(),
responstimeAllitems = new Dictionary<string, Dictionary<string, SortedSet<DateTime>>>(),
sharetrainitem =
new Dictionary<string,
Dictionary<string, SortedSet<DateTime>>>(); //初始化三个嵌套字典,记录分享用户信息、所有商品的响应时间信息和分享商品信息
var userclassinfo =
new Dictionary<string, Dictionary<string, Dictionary<string, int>>>(); //初始化一个嵌套字典结构,记录用户类别信息
var itemuserclassinfo =
new Dictionary<string, Dictionary<string, HashSet<string>>>(); //初始化一个嵌套字典结构,记录商品的用户类别信息
var shareresponse = new Dictionary<string, Dictionary<string, List<double>>>(); //初始化一个嵌套字典结构,记录分享和响应信息
var classtype = new Dictionary<string, string>(); //初始化一个字典,记录类别类型信息
var classtypeweight = new Dictionary<string, double>(); //初始化一个字典,记录类别权重信息
var classtypekey = new List<string>() { "Brand", "CateID", "CateOne", "Shop" }; //初始化一个列表,记录类别关键字信息
var weight = new List<double>() { 0.1, 0.1, 0.5, 0.3 }; //初始化一个列表,记录权重信息
n = 0; //重置n的值为0
foreach (var xid in classtypekey.ToList().WithProgressBar("Processing 1...")) //遍历类别关键字列表,执行以下操作:
{
classtype.Add(xid, ""); //将当前关键字和空字符串添加到类别类型字典中
classtypeweight.Add(xid, weight[n++]); //将当前关键字和对应的权重添加到类别权重字典中
}
n = 0; //重置n的值为0
foreach (var xid in classtype.Keys.WithProgressBar("Processing 2...")) //遍历类别类型字典的键,执行以下操作:
{
userclassinfo.Add(xid, new Dictionary<string, Dictionary<string, int>>()); //将当前键和一个新的嵌套字典添加到用户类别信息字典中
itemuserclassinfo.Add(xid, new Dictionary<string, HashSet<string>>()); //将当前键和一个新的嵌套字典添加到商品的用户类别信息字典中
}
// 这段代码的主要工作是遍历用户数据文件,并为每个用户创建和初始化各种属性和统计数据。这些属性和数据包括:
// 用户ID、等级、性别、年龄、邻居、新邻居、比率、响应时间、项目ID、响应时间区、静态相似用户、网络关系、响应项目、分享排名、分享数量、
// 响应数量、活跃数量、所有邻居、活跃频率以及用户响应时间。这段代码的策略主要是通过构建各种字典和集合,
// 对用户的属性和行为进行详细的追踪和统计,从而为之后的预测工作提供依据。
foreach (var temp in
userjsonData.Cast<JsonData>().ToList().WithProgressBar("Processing 用户数据...")) //遍历用户数据json文件
{
var user_id = temp[0]; //提取用户id
var level = temp[3]; //提取用户等级
id1 = user_id.ToString(); //将用户id转换为字符串
users.Add(id1, new UserIDInfo()); //将新用户添加到users字典中,并为其创建新的UserIDInfo实例
users[id1].SimLLusers = new Dictionary<string, double>(); //初始化字典来记录每个用户分享回流item的次数(如果分享一次,折算为2)
users[id1].SimFFusers = new Dictionary<string, double>(); //初始化字典来记录是否已分享,已分享记为1;
responstimeAllitems.Add(id1, new Dictionary<string, SortedSet<DateTime>>()); //初始化嵌套字典,存储用户的所有响应时间
responseall.Add(id1, new HashSet<string>()); //初始化集合,存储用户的所有响应
shareresponse.Add(id1, new Dictionary<string, List<double>>()); //初始化嵌套字典,存储用户分享和响应的信息
sharetrainitem.Add(id1, new Dictionary<string, SortedSet<DateTime>>()); //初始化嵌套字典,存储用户分享商品的信息
users[id1].ItemPath = new Dictionary<string, int>(); //初始化字典,存储用户的项目路径
users[id1].Level = int.Parse(level.ToString()); //将用户等级转换为整数并存储
users[id1].Gender = int.Parse(temp[1].ToString()); //将用户性别转换为整数并存储
users[id1].Age = int.Parse(temp[2].ToString()); //将用户年龄转换为整数并存储
users[id1].Neighbor = new Dictionary<string, List<DateTime>>(); //初始化字典,存储用户的邻居信息
users[id1].NewNeighbor = new Dictionary<string, List<DateTime>>(); //初始化字典,存储用户的新邻居信息
users[id1].Ratio = new Dictionary<int, Dictionary<string, double>>(); //初始化嵌套字典,存储用户的比率信息
users[id1].ResponesTime = new List<DateTime>(); //初始化列表,存储用户的响应时间信息
users[id1].ItemID = new HashSet<string>(); //初始化集合,存储用户的项目id信息
users[id1].responseTimeZone = new Dictionary<int, double>(); //初始化字典,存储用户的响应时间区信息
users[id1].StaticSimUsers = new Dictionary<string, double>(); //初始化字典,存储用户的静态相似用户信息
// 分享和被分享次数
users[id1].ShareNum = 0; //初始化分享次数为0
users[id1].BeShareNum = 0; //初始化响应次数为0
netrelation.Add(id1,
new Dictionary<string, Dictionary<int, Dictionary<string, double>>>()); //初始化嵌套字典,存储网络关系信息
responseitems.Add(id1, new Dictionary<string, HashSet<string>>()); //初始化嵌套字典,存储响应项目信息
sharerank.Add(id1, new Dictionary<string, double>()); //初始化字典,存储分享排名信息
sharenum.Add(id1, new HashSet<string>()); //初始化集合,存储分享数量信息
responsenum.Add(id1, new Dictionary<string, HashSet<string>>()); //初始化嵌套字典,存储响应数量信息
activenum.Add(id1, 0); //初始化字典,存储活跃数量信息,先将其设置为0
allNeigbors.Add(id1, new Dictionary<string, HashSet<string>>()); //初始化嵌套字典,存储所有邻居信息
allNeigbors[id1].Add("F", new HashSet<string>()); //初始化集合,存储一阶邻居信息
allNeigbors[id1].Add("L", new HashSet<string>()); //初始化集合,存储一阶邻居信息
allNeigbors[id1].Add("FF", new HashSet<string>()); //初始化集合,存储一阶邻居信息
allNeigbors[id1].Add("LF", new HashSet<string>()); //初始化集合,存储二阶邻居信息
allNeigbors[id1].Add("XF", new HashSet<string>()); //初始化集合,存储三阶邻居信息
activefrenq.Add(id1, 0); //初始化字典,存储活跃频率信息,先将其设置为0
userresptime.Add(id1, new List<DateTime>()); //初始化列表,存储用户响应时间信息
}
// 这段代码的主要工作是遍历商品数据文件,并为每个商品创建和初始化各种属性和统计数据。这些属性和数据包括:
// 商品ID、商店ID、品牌ID、分类ID和等级ID。与之前的用户数据处理类似,
// 这段代码的策略主要是通过构建字典来跟踪和统计商品的属性,从而为之后的预测工作提供依据。
foreach (var temp in
itemjsonData.Cast<JsonData>().ToList().WithProgressBar("Processing 商品数据...")) //遍历商品数据json文件
{
var item_id = temp[0]; //提取商品id
var cate_id = temp[1]; //提取分类id
var level_id = temp[2]; //提取等级id
var brandid = temp[3]; //提取品牌id
var shopid = temp[4]; //提取商店id
itemid = item_id.ToString(); //将商品id转换为字符串
itemsinfo.Add(itemid, new ItemInfo()); //将新商品添加到itemsinfo字典中,并为其创建新的ItemInfo实例
itemsinfo[itemid].ShopId = shopid.ToString(); //将商店id转换为字符串并存储
itemsinfo[itemid].BrandId = brandid.ToString(); //将品牌id转换为字符串并存储
itemsinfo[itemid].CateId = cate_id.ToString(); //将分类id转换为字符串并存储
itemsinfo[itemid].CateLevelOneId = level_id.ToString(); //将等级id转换为字符串并存储
itemreceive.Add(itemid, new Dictionary<string, HashSet<string>>()); //初始化嵌套字典,存储商品接收信息
//ItemAll.Add(itemid,new SortedDictionary<DateTime, List<Tuple<string, string>>>()); //此行代码被注释掉,本来用于初始化嵌套字典,存储商品所有信息
}
// 此段代码主要用于处理训练数据。在遍历训练数据的过程中,它会提取每个JSON对象中的user_id, item_id, voter_id, 和timestamp,
// 并使用这些信息来创建LinkInfo对象。然后,根据这些信息,它会更新多个字典结构,这些字典结构用于保存不同类型的用户和商品信息,
// 如回应时间,排名,分享数,回应数,活跃数和分类信息。这段代码使用了特征工程的方法,
// 通过提取和组合原始数据中的信息来生成用于预测voter_id的特征。在遍历结束后,每个用户和商品都会有一个与其相关的详细信息集,
// 这些信息将用于预测阶段。
foreach (var temp in jsonData.Cast<JsonData>().ToList().WithProgressBar("Processing 训练数据...")) //遍历训练数据
{
var user_id = temp["inviter_id"]; //提取用户id
var item_id = temp["item_id"]; //提取物品id
var voter_id = temp["voter_id"]; //提取投票者id
var timestamp = temp["timestamp"]; //提取时间戳
lk = new LinkInfo(); //创建新的LinkInfo实例
id1 = lk.UserID = user_id.ToString(); //将用户id转换为字符串并赋值给UserID
item = lk.ItemID = item_id.ToString(); //将物品id转换为字符串并赋值给ItemID
id2 = lk.VoterID = voter_id.ToString(); //将投票者id转换为字符串并赋值给VoterID
// 更新id1用户的分享次数
users[id1].ShareNum++; //用户分享次数加1
// 更新id2用户的回应次数
users[id2].BeShareNum++; //用户回应次数加1
dt = DateTime.Parse(timestamp.ToString()); //将时间戳转换为DateTime对象
if (!responstimeAllitems[id2].ContainsKey(item))
responstimeAllitems[id2].Add(item, new SortedSet<DateTime>()); //如果回应时间列表中没有该项,就添加
responstimeAllitems[id2][item].Add(dt); //在回应时间列表中添加时间戳
if (!data.ContainsKey(dt)) data.Add(dt, new List<LinkInfo>()); //如果数据中没有该时间,就添加
data[dt].Add(lk); //在数据中添加LinkInfo对象
if (!ranks.ContainsKey(id1))
ranks.Add(id1, new Dictionary<string, SortedDictionary<DateTime, List<string>>>()); //如果排名中没有该用户,就添加
if (!ranks[id1].ContainsKey(item))
ranks[id1].Add(item, new SortedDictionary<DateTime, List<string>>()); //如果用户中没有该物品,就添加
if (!ranks[id1][item].ContainsKey(dt)) ranks[id1][item].Add(dt, new List<string>()); //如果物品中没有该时间,就添加
ranks[id1][item][dt].Add(id2); //在指定时间和物品中添加投票者id
sharenum[id1].Add(item); //在分享数中添加物品id
if (!responsenum[id1].ContainsKey(id2))
responsenum[id1].Add(id2, new HashSet<string>()); //如果回应数中没有该投票者,就添加
responsenum[id1][id2].Add(item); //在指定投票者中添加物品id
activenum[id2] += 1; //增加投票者活动数
classtype["Brand"] = itemsinfo[item].BrandId; //获取商品的品牌id
classtype["CateID"] = itemsinfo[item].CateId; //获取商品的分类id
classtype["CateOne"] = itemsinfo[item].CateLevelOneId; //获取商品的一级分类id
classtype["Shop"] = itemsinfo[item].ShopId; //获取商品的商店id
foreach (var xid in classtype.Keys) //遍历分类类型
{
if (!userclassinfo[xid].ContainsKey(id1))
userclassinfo[xid].Add(id1, new Dictionary<string, int>()); //如果用户分类信息中没有该用户,就添加
if (!userclassinfo[xid][id1].ContainsKey(classtype[xid]))
userclassinfo[xid][id1].Add(classtype[xid], 0); //如果用户分类信息中没有该分类,就添加
userclassinfo[xid][id1][classtype[xid]]++; //增加指定分类的计数
if (!userclassinfo[xid].ContainsKey(id2))
userclassinfo[xid].Add(id2, new Dictionary<string, int>()); //如果用户分类信息中没有该投票者,就添加
if (!userclassinfo[xid][id2].ContainsKey(classtype[xid]))
userclassinfo[xid][id2].Add(classtype[xid], 0); //如果投票者分类信息中没有该分类,就添加
userclassinfo[xid][id2][classtype[xid]]++; //增加指定分类的计数
if (!itemuserclassinfo[xid].ContainsKey(classtype[xid]))
itemuserclassinfo[xid].Add(classtype[xid], new HashSet<string>()); //如果物品用户分类信息中没有该分类,就添加
itemuserclassinfo[xid][classtype[xid]].Add(id1); //在指定分类中添加用户id
itemuserclassinfo[xid][classtype[xid]].Add(id2); //在指定分类中添加投票者id
}
++n; //增加计数器
}
// 储存仅分享用户
var shareOnlyUsers = new HashSet<string>(); //创建仅分享用户集合
foreach (var user_id in users.Keys) //遍历用户
{
if (users[user_id].BeShareNum == 0) //如果用户的回应次数为0
{
if(users[user_id].ShareNum > 30)
shareOnlyUsers.Add(user_id); //将用户添加到仅分享用户集合中
}
}
jsonData.Clear();
userjsonData.Clear();
itemjsonData.Clear();
// 此代码段主要用于计算每个用户对每个物品的分享排名。它遍历ranks字典,该字典保存了每个用户对每个物品在不同日期的投票情况。
// 然后,对于每个用户、物品和日期,计算一个排名分数,该分数为1除以ii和tt的乘积,其中ii为指定用户、物品和日期的投票者数量,tt始终为1。
// 这个分数加入到sharerank字典中,该字典保存了每个用户对每个投票者的总排名分数。然后,它初始化了几个字典,这些字典用于保存后续需要使用的数据。
// 最后,它计算了data字典中的最大和最小日期。
foreach (var id in ranks.Keys.WithProgressBar("Processing 3...")) //遍历所有的用户id
{
double tt = 1; //初始化tt值为1
foreach (var fid in ranks[id].Keys) //遍历指定用户id对应的所有物品id
{
double ii = 1; //初始化ii值为1
foreach (var d in ranks[id][fid].Keys) //遍历指定用户id和物品id对应的所有日期
{
foreach (var xid in ranks[id][fid][d]) //遍历指定用户id、物品id和日期对应的所有投票者id
if (!sharerank[id].ContainsKey(xid))
sharerank[id].Add(xid, 1.0 / ii / tt); //如果分享排名中没有指定的投票者id,就添加并初始化评分
else sharerank[id][xid] += 1.0 / ii / tt; //如果分享排名中已有指定的投票者id,就累加评分
ii += ranks[id][fid][d].Count; //累加指定用户id、物品id和日期对应的投票者数量
}
}
}
Dictionary<string, Dictionary<string, HashSet<string>>> dataLF =
new Dictionary<string,
Dictionary<string, HashSet<string>>>(), //初始化dataLF,键为用户id,值为一个字典,该字典的键为物品id,值为投票者id的集合
dataItemLF =
new Dictionary<string,
Dictionary<string, HashSet<string>>>(); //初始化dataItemLF,键为物品id,值为一个字典,该字典的键为用户id,值为投票者id的集合
var items = new Dictionary<string, List<DateTime>>(); //初始化items,键为物品id,值为日期的列表
var itemusers = new Dictionary<string, HashSet<string>>(); //初始化itemusers,键为物品id,值为用户id的集合
DateTime dtmax = data.Keys.Max(), dtmin = data.Keys.Min(); //找出data中的最大和最小日期
// 此段代码主要在处理数据集中的所有链接信息,并将处理的结果存储到多个字典中。这些字典包括:用户的响应时间、用户的响应物品、用户的分享训练物品、用户的邻居、
// 用户的网络关系、用户的物品ID、物品的日期、物品的用户、物品的接收者、用户的活动频率、用户的分享响应、用户的响应物品以及用户的所有邻居。
//此代码使用了几种数据结构:SortedSet < DateTime >(排序的日期集合)、Dictionary<int, Dictionary<string, double>>(用户之间的网络关系),以及一些其他用于存储用户、物品和日期的字典。
//在处理过程中,代码对每种物品信息(如类别一级ID、类别ID、商店ID和品牌ID)的出现次数进行了统计,并计算了分享响应的衰减函数。此外,代码还对投票者在过去30天内的活动频率进行了统计。
//此代码的核心策略是通过遍历数据集中的所有链接信息,提取并统计各种信息,以便于后续的预测任务。
foreach (var d in data.Keys.WithProgressBar("Processing 4...")) //遍历所有的日期
foreach (var llk in data[d]) //遍历指定日期的所有链接信息
{
itemid = llk.ItemID; //获取物品ID
id1 = llk.UserID; //获取用户ID
id2 = llk.VoterID; //获取投票者ID
userresptime[id2].Add(d); //将日期添加到指定投票者的响应时间
responseall[id2].Add(itemid); //将物品ID添加到指定投票者的响应集合中
if (!sharetrainitem[id1].ContainsKey(itemid)) //如果分享训练物品字典中指定用户对应的字典没有指定物品ID
sharetrainitem[id1].Add(itemid, new SortedSet<DateTime>()); //则添加物品ID并初始化日期集合
sharetrainitem[id1][itemid].Add(d); //将日期添加到指定用户和物品ID的分享训练物品集合中
if (!users[id1].Neighbor.ContainsKey(id2)) //如果指定用户的邻居字典没有指定投票者ID
{
netrelation[id1]
.Add(id2, new Dictionary<int, Dictionary<string, double>>()); //在网络关系字典中添加投票者ID并初始化内层字典
users[id1].Neighbor.Add(id2, new List<DateTime>()); //在用户字典的邻居字典中添加投票者ID并初始化日期列表
}
for (var ii = 0; ii < 4; ++ii) //遍历0到3的所有数字
if (!netrelation[id1][id2].ContainsKey(ii)) //如果网络关系字典中指定用户和投票者对应的字典没有指定数字
netrelation[id1][id2].Add(ii, new Dictionary<string, double>()); //则添加数字并初始化内层字典
//以下四个if-else结构对应四种物品信息:类别一级ID、类别ID、商店ID和品牌ID,用于在网络关系字典中统计各类物品信息的出现次数
if (!netrelation[id1][id2][0].ContainsKey(itemsinfo[itemid].CateLevelOneId))
netrelation[id1][id2][0].Add(itemsinfo[itemid].CateLevelOneId, 1);
else netrelation[id1][id2][0][itemsinfo[itemid].CateLevelOneId]++;
if (!netrelation[id1][id2][1].ContainsKey(itemsinfo[itemid].CateId))
netrelation[id1][id2][1].Add(itemsinfo[itemid].CateId, 1);
else netrelation[id1][id2][1][itemsinfo[itemid].CateId]++;
if (!netrelation[id1][id2][2].ContainsKey(itemsinfo[itemid].ShopId))
netrelation[id1][id2][2].Add(itemsinfo[itemid].ShopId, 1);
else netrelation[id1][id2][2][itemsinfo[itemid].ShopId]++;
if (!netrelation[id1][id2][3].ContainsKey(itemsinfo[itemid].BrandId))
netrelation[id1][id2][3].Add(itemsinfo[itemid].BrandId, 1);
else netrelation[id1][id2][3][itemsinfo[itemid].BrandId]++;
//添加日期到指定用户和投票者的邻居日期列表,添加日期到指定投票者的响应时间列表,添加物品ID到指定用户和投票者的物品ID列表
users[id1].Neighbor[id2].Add(d);
users[id2].ResponesTime.Add(d);
users[id1].ItemID.Add(llk.ItemID);
users[id2].ItemID.Add(llk.ItemID);
if (!items.ContainsKey(llk.ItemID)) //如果物品字典没有指定物品ID
items.Add(llk.ItemID, new List<DateTime>()); //则添加物品ID并初始化日期列表
items[llk.ItemID].Add(d); //将日期添加到指定物品ID的日期列表中
//以下三个if结构对应物品用户字典、物品接收字典和响应物品字典,用于将指定用户和/或投票者添加到指定物品ID的相关字典中
if (!itemusers.ContainsKey(itemid)) itemusers.Add(itemid, new HashSet<string>());
itemusers[itemid].Add(id1);
itemusers[itemid].Add(id2);
if (!itemreceive[itemid].ContainsKey(id1)) itemreceive[itemid].Add(id1, new HashSet<string>());
if ((dtmax - d).TotalDays < 30) activefrenq[id2]++;
itemreceive[itemid][id1].Add(id2);
if (!responseitems[id2].ContainsKey(id1)) responseitems[id2].Add(id1, new HashSet<string>());
if (!shareresponse[id2].ContainsKey(id1)) shareresponse[id2].Add(id1, new List<double>());
shareresponse[id2][id1].Add(Math.Exp(-0.1 * (d - sharetrainitem[id1][itemid].Min()).TotalHours));
responseitems[id2][id1].Add(itemid);
allNeigbors[id1]["F"].Add(id2);
allNeigbors[id2]["L"].Add(id1);
}
// 该段代码主要做了两件事。首先,它归一化了所有用户的活动频率。这是通过获取最大活动频率并将每个用户的活动频率除以最大活动频率实现的。
// 其次,代码遍历了网络关系字典,对其中的所有用户-邻居-物品信息进行归一化处理。它计算了指定用户和邻居用户的所有物品信息的总值,并将每个物品的信息除以总值,从而实现归一化。
// 在数据预处理的阶段,归一化是一种常见的方法,用来消除不同数据之间的量纲和尺度差异,使其落在同一数量级,便于进行后续的分析和处理。
// 创建一个列表用于存储所有在活跃度字典中出现的用户ID
var strlist = activefrenq.Keys.ToList();
// 找到活跃度字典中的最大值
var actmax = activefrenq.Values.Max();
// 遍历活跃度字典中的所有用户ID,并对每个用户的活跃度进行归一化处理
foreach (var iid in strlist.WithProgressBar("Processing 5..."))
activefrenq[iid] = (activefrenq[iid] + 0.001) / actmax;
// 遍历网络关系字典中的所有用户ID
foreach (var iid in netrelation.Keys.WithProgressBar("Processing 6..."))
// 遍历指定用户的所有邻居用户
foreach (var fid in netrelation[iid].Keys)
// 遍历指定用户和邻居用户的所有物品信息
foreach (var xid in netrelation[iid][fid].Keys)
{
// 计算指定用户和邻居用户的所有物品信息的总值
var yy = netrelation[iid][fid][xid].Values.Sum();
// 创建一个列表用于存储所有在物品信息中出现的物品ID
var tmparr = new List<string>(netrelation[iid][fid][xid].Keys);
// 遍历物品信息中的所有物品ID,并对每个物品的信息进行归一化处理
foreach (var mid in tmparr)
netrelation[iid][fid][xid][mid] = netrelation[iid][fid][xid][mid] * 1.0 / yy;
}
// 声明两个整型变量 k1 和 k2,以及一个双精度浮点型变量 sim
int k1, k2;
double sim;
// 这段代码主要实现的功能是找出每个用户的二阶邻居和三阶邻居,其中"F"代表一阶邻居,"L"代表二阶邻居,"FF"代表二阶邻居,"LF"也代表二阶邻居,
// "XF"代表三阶邻居。具体操作如下:对于每个用户,遍历其所有一阶邻居,然后遍历这些一阶邻居的所有一阶邻居,得到的就是用户的二阶邻居,
// 分别存储在"FF"和"LF"下,然后从二阶邻居中删除所有的一阶邻居;接着,遍历二阶邻居的所有一阶邻居,得到的就是用户的三阶邻居,存储在"XF"下,
// 最后从三阶邻居中删除所有的一阶和二阶邻居。这样做的目的是将网络中的节点按照与给定用户的关系远近进行分类,为下一步的特征工程提供了基础。
// 遍历所有的邻居列表
foreach (var id in allNeigbors.Keys.WithProgressBar("Processing 7..."))
{
// 遍历当前用户的所有一阶邻居
foreach (var fid in allNeigbors[id]["F"])
{
// 从当前用户的一阶邻居中删除该邻居
allNeigbors[id]["L"].Remove(fid);
// 遍历该一阶邻居的所有邻居,并将其添加到当前用户的二阶邻居中
foreach (var xid in allNeigbors[fid]["F"]) allNeigbors[id]["FF"].Add(xid);
foreach (var xid in allNeigbors[fid]["L"]) allNeigbors[id]["LF"].Add(xid);
}
// 为当前用户查找二阶邻居
foreach (var fid in allNeigbors[id]["L"])
{
// 遍历该一阶邻居的所有邻居,并将其添加到当前用户的二阶邻居中
foreach (var xid in allNeigbors[fid]["F"]) allNeigbors[id]["LF"].Add(xid);
foreach (var xid in allNeigbors[fid]["L"]) allNeigbors[id]["LF"].Add(xid);
}
// 从当前用户的二阶邻居中删除所有的一阶邻居
foreach (var fid in allNeigbors[id]["F"])
{
allNeigbors[id]["LF"].Remove(fid);
allNeigbors[id]["FF"].Remove(fid);
}
// 从当前用户的二阶邻居中删除所有的一阶邻居
foreach (var fid in allNeigbors[id]["L"])
allNeigbors[id]["LF"].Remove(fid);
// 遍历当前用户的所有二阶邻居
foreach (var fid in allNeigbors[id]["FF"])
{
// 遍历该二阶邻居的所有邻居,并将其添加到当前用户的三阶邻居中
foreach (var xid in allNeigbors[fid]["F"]) allNeigbors[id]["XF"].Add(xid);
foreach (var xid in allNeigbors[fid]["L"]) allNeigbors[id]["XF"].Add(xid);
}
// 为当前用户查找三阶邻居
foreach (var fid in allNeigbors[id]["LF"])
{
// 遍历该二阶邻居的所有邻居,并将其添加到当前用户的三阶邻居中
foreach (var xid in allNeigbors[fid]["F"]) allNeigbors[id]["XF"].Add(xid);
foreach (var xid in allNeigbors[fid]["L"]) allNeigbors[id]["XF"].Add(xid);
}
// 从当前用户的三阶邻居中删除所有的一阶和二阶邻居
foreach (var fid in allNeigbors[id]["F"])
allNeigbors[id]["XF"].Remove(fid);
foreach (var fid in allNeigbors[id]["L"])
allNeigbors[id]["XF"].Remove(fid);
foreach (var fid in allNeigbors[id]["LF"])
allNeigbors[id]["XF"].Remove(fid);
foreach (var fid in allNeigbors[id]["FF"])
allNeigbors[id]["XF"].Remove(fid);
}
//double mmr = 0, ktotal = 0;
// 定义变量
double tsp, rsim, ritem;
// 定义接收数据字典,用于存储字符串和HashSet<string>
var receivedata = new Dictionary<string, HashSet<string>>();
// 定义提交结果列表,用于存储提交信息
var submitres = new List<SubmitInfo>();
// 定义提交信息临时变量
SubmitInfo subtemp;
// 创建一个StreamWriter用于写入"testres.txt"文件
var ppr = new System.IO.StreamWriter("testres.txt");
// 定义共享项字典
var shareitems = new Dictionary<string, Dictionary<string, List<DateTime>>>();
// 定义共享详情字典
var sharedetails = new Dictionary<string, Dictionary<string, List<DateTime>>>();
// 定义共享ID字典
var shareIDs = new Dictionary<string, Dictionary<string, List<DateTime>>>();
// 定义最后分享的字典,存储字符串和DateTime
var lastshare = new Dictionary<string, DateTime>();
// 定义共享次数字典
var shareNumber = new Dictionary<string, Dictionary<string, int>>();
// 定义测试ID的HashSet
var IDTest = new HashSet<string>();
// 这段代码主要用于处理测试数据。它读取测试数据,然后提取出各个数据项(如id1,itemid,testdate等),
// 并将这些数据项添加到相应的数据结构中,如shareitems,shareIDs,lastshare,sharetimes,shareusers,sharedetails等。
// 这些数据结构用于在后续代码中进行进一步的处理和分析。这段代码的主要策略就是通过将测试数据存储在多个数据结构中,
// 以便能够从多个角度对数据进行分析,这样可以更好地进行特征工程。
// 遍历测试数据
foreach (var testdata in testjsonData.Cast<JsonData>().ToList().WithProgressBar("Processing 测试数据..."))
{
id1 = testdata[1].ToString(); // 获取id1
itemid = testdata[2].ToString(); // 获取itemid
var testdate = DateTime.Parse(testdata[3].ToString()); // 获取并解析日期
// 如果shareitems字典不包含id1,则添加
if (!shareitems.ContainsKey(id1)) shareitems.Add(id1, new Dictionary<string, List<DateTime>>());
// 如果shareitems[id1]不包含itemid,则添加
if (!shareitems[id1].ContainsKey(itemid)) shareitems[id1].Add(itemid, new List<DateTime>());
// 将测试日期添加到shareitems[id1][itemid]的列表中
shareitems[id1][itemid].Add(testdate);
// 添加id1到IDTest HashSet中
IDTest.Add(id1);
// 如果shareIDs字典不包含itemid,则添加
if (!shareIDs.ContainsKey(itemid))
{
shareIDs.Add(itemid, new Dictionary<string, List<DateTime>>());
shareNumber.Add(itemid, new Dictionary<string, int>());
}
// 如果shareIDs[itemid]不包含id1,则添加
if (!shareIDs[itemid].ContainsKey(id1))
{
shareIDs[itemid].Add(id1, new List<DateTime>());
shareNumber[itemid].Add(id1, 0);
}
// 将测试日期添加到shareIDs[itemid][id1]的列表中,并增加shareNumber[itemid][id1]的次数
shareIDs[itemid][id1].Add(testdate);
shareNumber[itemid][id1]++;
// 如果lastshare字典不包含itemid,则添加,否则更新lastshare[itemid]为测试日期
if (!lastshare.ContainsKey(itemid)) lastshare.Add(itemid, testdate);
else lastshare[itemid] = testdate;
// 如果sharetimes字典不包含itemid,则添加,否则增加sharetimes[itemid]的次数
if (!sharetimes.ContainsKey(itemid)) sharetimes.Add(itemid, 1);
else sharetimes[itemid]++;
// 如果shareusers字典不包含itemid,则添加
if (!shareusers.ContainsKey(itemid))
shareusers.Add(itemid, new Dictionary<string, SortedSet<DateTime>>());
// 如果shareusers[itemid]不包含id1,则添加
if (!shareusers[itemid].ContainsKey(id1))
shareusers[itemid].Add(id1, new SortedSet<DateTime>());
// 将测试日期添加到shareusers[itemid][id1]的SortedSet中
shareusers[itemid][id1].Add(testdate);
// 如果sharedetails字典不包含itemid,则添加
if (!sharedetails.ContainsKey(itemid))
sharedetails.Add(itemid, new Dictionary<string, List<DateTime>>());
// 如果sharedetails[itemid]不包含id1,则添加
if (!sharedetails[itemid].ContainsKey(id1)) sharedetails[itemid].Add(id1, new List<DateTime>());
// 将测试日期添加到sharedetails[itemid][id1]的列表中
sharedetails[itemid][id1].Add(testdate);
}
// 这段代码主要是进行了一些特征工程的工作,对用户的行为和特性进行了一些统计和计算,主要包括计算用户的响应频率、计算用户在近30天内的响应次数,统计用户在不同类别上的行为分布并计算了比例。
// 并且对用户频率和用户时间进行了归一化处理。这些特征都可能对预测voter_id有所帮助。
// 定义用户频率和用户时间字典
Dictionary<string, double> userfreq = new Dictionary<string, double>(),
usertimes = new Dictionary<string, double>();
// 遍历用户
foreach (var iid in users.Keys.WithProgressBar("Processing 8..."))
{
// 如果用户响应时间有值,将差距最大的天数添加到用户频率字典中
if (userresptime[iid].Count > 0)
userfreq.Add(iid, (dtmax - userresptime[iid].Max()).TotalDays);
// 否则将最大和最小日期之间的天数添加到用户频率字典中
else userfreq.Add(iid, (dtmax - dtmin).TotalDays);
// 将0添加到用户时间字典中
usertimes.Add(iid, 0);
// 遍历用户响应时间,如果最大日期和响应日期之间的天数小于30,用户时间增加1
foreach (var dx in userresptime[iid])
if ((dtmax - dx).TotalDays < 30)
usertimes[iid]++;
// 定义类别计数字典
var catenum = new Dictionary<int, Dictionary<string, int>>();
for (var ii = 0; ii < 4; ++ii)
{
// 对每个类别添加字典
catenum.Add(ii, new Dictionary<string, int>());
users[iid].Ratio.Add(ii, new Dictionary<string, double>());
}
// 遍历用户的ItemID
foreach (var fid in users[iid].ItemID)
{
// 对每个类别的项进行计数,如果不存在则添加
if (!catenum[0].ContainsKey(itemsinfo[fid].CateLevelOneId))
catenum[0].Add(itemsinfo[fid].CateLevelOneId, 1);
else catenum[0][itemsinfo[fid].CateLevelOneId]++;
if (!catenum[1].ContainsKey(itemsinfo[fid].CateId))
catenum[1].Add(itemsinfo[fid].CateId, 1);
else catenum[1][itemsinfo[fid].CateId]++;
if (!catenum[2].ContainsKey(itemsinfo[fid].ShopId))
catenum[2].Add(itemsinfo[fid].ShopId, 1);
else catenum[2][itemsinfo[fid].ShopId]++;
if (!catenum[3].ContainsKey(itemsinfo[fid].BrandId))
catenum[3].Add(itemsinfo[fid].BrandId, 1);
else catenum[3][itemsinfo[fid].BrandId]++;
}
// 定义临时变量tt用于计算比例
double tt;
for (var ii = 0; ii < 4; ++ii)
{
// 计算总数
tt = catenum[ii].Values.Sum();
// 计算每个类别的比例并添加到用户的比例中
foreach (var xx in catenum[ii].Keys) users[iid].Ratio[ii].Add(xx, catenum[ii][xx] * 1.0 / tt);
}
}
// 计算用户频率和用户时间的最大值
double freqmax = userfreq.Values.Max(), timesmax = usertimes.Values.Max();
// 用最大值来归一化用户频率和用户时间
foreach (var iid in users.Keys.WithProgressBar("Processing 9..."))
{
userfreq[iid] /= freqmax;
usertimes[iid] /= timesmax;
}
// 定义两个变量用于后续操作
int kx = 0, kn = users.Count;
// 这段代码主要执行了用户相似性的计算,主要基于用户的行为和行为交集进行相似度计算。
// 对于每个测试用户,它首先找到与之交互过相同项目的所有用户,并根据公式计算了与这些用户的相似度。
// 另外,如果两个用户是邻居,则通过另一个公式计算其相似度。
// 这个计算过程包含了项目交集、邻居交集等多个因素,使得相似度更能反映用户的实际相似情况。
// 通过这样的特征工程,模型可能更好地预测voter_id。
// 定义一个存储用户之间相似性的字典
var SimNeigbor = new Dictionary<string, Dictionary<string, double>>();
// 创建一个临时的HashSet用于存储交集
var NTemp = new HashSet<string>();
// 遍历测试ID列表
foreach (var iid in IDTest.ToList().WithProgressBar("Processing 10..."))
{
//if (!users.ContainsKey(iid))
//{
// Console.WriteLine(iid+":error 549");
// continue;
//}
// 更新进度条
if (++kx % 100 == 0) Text = kx.ToString() + " " + kn.ToString();
// 刷新应用
Application.DoEvents();
// 为当前用户初始化相似的用户集
users[iid].SimLusers = new Dictionary<string, double>();
users[iid].SimFusers = new Dictionary<string, double>();
// 创建一个排序字典用于存储用户之间的相似度
var simUser = new SortedDictionary<double, List<string>>();
// 创建一个HashSet用于存储相关的用户
var backusers = new HashSet<string>();
foreach (var itemiid in users[iid].ItemID)
foreach (var zid in itemusers[itemiid])
backusers.Add(zid);
// 循环遍历相关用户
foreach (var fid in backusers)
{
// 如果用户ID相同则跳过
if (fid == iid) continue;
// 计算用户间共享的项目数量
NTemp = users[iid].ItemID.Intersect(users[fid].ItemID).ToHashSet();
k2 = NTemp.Count();
if (k2 == 0) continue;
int k21 = 0, k22 = 0;
// 计算与邻居的交集数量
if (allNeigbors[iid].ContainsKey("F"))
k21 = NTemp.Intersect(allNeigbors[iid]["F"]).Count();
if (allNeigbors[iid].ContainsKey("L"))
k22 = NTemp.Intersect(allNeigbors[iid]["L"]).Count();
// 计算用户间总的项目数量
k1 = users[iid].ItemID.Union(users[fid].ItemID).Count();
// 计算相似度
sim = -(k21 * 1.2 + k22 * 0.2 + (k2 - k21 - k22) * 0.1) / k1 * (1 - 0.5 / Math.Sqrt(k1));
// 如果两个用户是邻居
if (users[iid].Neighbor.ContainsKey(fid))
{
k1 = sharenum[iid].Count;
k2 = responsenum[iid][fid].Count;
if (!SimNeigbor.ContainsKey(iid)) SimNeigbor.Add(iid, new Dictionary<string, double>());
// 计算并存储邻居间的相似度
var sk = k2 * 1.0 / k1 * (1 - 0.5 / Math.Sqrt(k1));
SimNeigbor[iid].Add(fid, sk);
}
// 将相似度和用户添加到字典中
if (!simUser.ContainsKey(sim)) simUser.Add(sim, new List<string>());
simUser[sim].Add(fid);
}
// 遍历simUser字典并添加到SimLusers字典中
foreach (var dd in simUser.Keys)
foreach (var fid in simUser[dd])
users[iid].SimLusers.Add(fid, -dd);
}
// 这段代码主要在进行准备工作,为预测模型准备所需的各种数据结构,
// 包括记录不同数量的字典、添加数量的字典、订阅内容的字典、已经添加的内容的字典、
// 回溯添加的内容的字典、已经添加的数量的字典、用户分类数量的字典等。
// 同时,它还为每个类别类型统计了用户的分类信息的数量。
// 通过对用户的分类信息进行详细的统计和记录,可以为预测模型提供更全面的数据,进而提高预测的准确性。
// 设置候选数量为6
var candNum = 6;
// 创建一个记录添加数量的字典
var AddNum = new Dictionary<string, Dictionary<string, int>>();
// 创建一个新的字典,用于存储订阅内容
var subnew = new Dictionary<string, List<string>>();
// 创建字典,用于存储已经添加的内容和回溯添加的内容
Dictionary<string, Dictionary<string, HashSet<string>>> haveadded =
new Dictionary<string, Dictionary<string, HashSet<string>>>(),
backadded = new Dictionary<string, Dictionary<string, HashSet<string>>>();
// 创建一个字典用于记录已经添加的数量
var AddedNumber = new Dictionary<string, Dictionary<string, int>>();
// 创建一个字典用于存储用户分类数量
var usersClassNum = new Dictionary<string, Dictionary<string, int>>();
// 遍历所有的类别类型
foreach (var xid in classtype.Keys.WithProgressBar("Processing 11..."))
{
// 对于每个类别,记录用户的分类信息的数量
usersClassNum.Add(xid, new Dictionary<string, int>());
foreach (var id in userclassinfo[xid].Keys)
usersClassNum[xid].Add(id, userclassinfo[xid][id].Values.Sum());
}
// 创建一个变量ch
var ch = 0;
// 创建一个新的HashSet用于存储布尔用户
var boolusers = new HashSet<string>();
int totalRemovals = 0;
// 从用户的相似用户中删除shareOnly用户
foreach (var user in users.Values)
{
totalRemovals += RemoveShareOnlyUsers(user.SimFusers);
totalRemovals += RemoveShareOnlyUsers(user.SimLusers);
totalRemovals += RemoveShareOnlyUsers(user.SimFFusers);
totalRemovals += RemoveShareOnlyUsers(user.SimLLusers);
}
Console.WriteLine($"Total removals: {totalRemovals}");
// Helper function to remove shareOnlyUsers
int RemoveShareOnlyUsers(Dictionary<string, double> simUsers)
{
if (simUsers == null || shareOnlyUsers == null)
{
return 0;
}
// 在一次操作中找出和删除所有 shareOnlyUsers
var keysToRemove = simUsers.Keys.Where(shareOnlyUsers.Contains).ToList();
foreach (var key in keysToRemove)
{
simUsers.Remove(key);
}
return keysToRemove.Count; // 返回这个函数删除的元素数量
}
Console.WriteLine("删除了"+totalRemovals+"个用户");
data.Clear();
// 这段代码首先遍历了测试数据集,对于每一条测试数据,它都会解析出相应的用户ID和物品ID,然后在几个关键字典(例如AddNum和receivedata)中为这两个ID添加或更新记录。
// 接着,如果用户字典(users)中存在这个用户ID,那么它会为这个用户初始化几个用于存储各种得分的排序字典。
// 最后,它会计算并设置一个名为att的属性,它依赖于AddNum字典中某用户ID和项目ID的数量。这部分代码的
// 遍历测试数据集
foreach (var testdata in testjsonData.Cast<JsonData>().ToList().WithProgressBar("Processing 结果输出..."))
{
// 每处理100条数据更新一次显示的文本
if (++ch % 100 == 0) Text = ch.ToString();
// 进行界面更新
Application.DoEvents();
// 创建一个结果列表
var res = new List<string>();
// 创建一个链接信息的对象
var linfo = new LinkInfo();
// 设置对象的用户ID和物品ID
id1 = linfo.UserID = testdata[1].ToString();
itemid = linfo.ItemID = testdata[2].ToString();
// 解析测试日期
var testdate = DateTime.Parse(testdata[3].ToString());
// 在AddNum字典中为该用户ID添加新的项目ID键,如果该用户ID不存在,则先添加该用户ID
if (!AddNum.ContainsKey(id1)) AddNum.Add(id1, new Dictionary<string, int>());
if (!AddNum[id1].ContainsKey(itemid)) AddNum[id1].Add(itemid, 1);
else AddNum[id1][itemid]++;
// 在接收数据字典中为该项目ID添加新的用户ID,如果该项目ID不存在,则先添加该项目ID
if (!receivedata.ContainsKey(itemid)) receivedata.Add(itemid, new HashSet<string>());
// 如果用户字典中不包含此用户ID,则跳过此次循环
if (!users.ContainsKey(id1)) continue;
// 初始化排序字典,用于存储各种得分
SortedDictionary<double, List<string>> scor = new SortedDictionary<double, List<string>>(),
scorF = new SortedDictionary<double, List<string>>(),
scorFF = new SortedDictionary<double, List<string>>(),
scorL = new SortedDictionary<double, List<string>>(),
scorX = new SortedDictionary<double, List<string>>(),
scorXF = new SortedDictionary<double, List<string>>(),
scorT1 = new SortedDictionary<double, List<string>>(),
scorT2 = new SortedDictionary<double, List<string>>(),
scorT3 = new SortedDictionary<double, List<string>>();
// 计算并设置属性att,它依赖于AddNum字典中某用户ID和项目ID的数量
var att = AddNum[id1][itemid] / 5;
if (AddNum[id1][itemid] % 5 == 0) att *= 5;
else att = att * 5 + 5;
// 这段代码中,为用户找到了相似的其他用户,并用权重来衡量这种相似性。
// 首先检查了用户的已知相似用户数量是否小于预定值(5或att的值)并且该用户是否尚未处理。
// 然后对用户的类别进行遍历,找出该类别中和当前用户有相似特征的用户(称之为反向用户),并将他们的相似性(根据他们在这个类别下的共享特征计算得出)添加到反向用户字典中。
// 最后,对反向用户进行遍历,计算他们的加权相似度(根据类别的权重计算得出),并将这个值添加到用户的相似用户字典中。
// 这个过程是一个特征工程的过程,为预测模型创建和添加了新的特征。
// 检查用户的相似用户数量是否小于5和属性att中的较大值,以及这个用户ID是否还没有被处理过
if (users[id1].SimLusers.Count < Math.Max(5, att) && !boolusers.Contains(id1))
{
// 将用户ID添加到处理过的用户集合中
boolusers.Add(id1);
// 进行界面更新
Application.DoEvents();
// 创建一个字典用于保存反向用户信息
var backuser = new Dictionary<string, Dictionary<string, double>>();
// 遍历每个类别
foreach (var mid in classtype.Keys)
{
// 如果用户类别信息中没有这个类别和用户ID的键值对,就添加进去
if (!userclassinfo[mid].ContainsKey(id1))
userclassinfo[mid].Add(id1, new Dictionary<string, int>());
// 创建一个HashSet用于保存反向临时用户
var backtmp = new HashSet<string>();
// 遍历每个用户ID在某一类别下的所有项目ID,然后添加到反向临时用户集合中
foreach (var xid in userclassinfo[mid][id1].Keys)
foreach (var fid in itemuserclassinfo[mid][xid])
backtmp.Add(fid);
// 从反向临时用户集合中移除当前用户已经有的相似用户,然后移除当前用户自己
backtmp = new HashSet<string>(backtmp.Except(users[id1].SimLusers.Keys));
backtmp.Remove(id1);
// 遍历反向临时用户集合
foreach (var fid in backtmp)
{
// 如果反向用户字典中没有这个用户ID,就添加进去
if (!backuser.ContainsKey(fid)) backuser.Add(fid, new Dictionary<string, double>());
// 计算两个用户在某一类别下的相同项目数和总项目数,然后计算他们的相似度
k2 = userclassinfo[mid][id1].Keys.Intersect(userclassinfo[mid][fid].Keys).Count();
if (k2 == 0) continue;
k1 = userclassinfo[mid][id1].Keys.Union(userclassinfo[mid][fid].Keys).Count();
sim = k2 * 1.0 / k1 * (1 - 0.5 / Math.Sqrt(k1));
// 将类别和相似度添加到反向用户字典中
backuser[fid].Add(mid, sim);
}
}
// 遍历反向用户字典
foreach (var fid in backuser.Keys)
{
// 计算加权后的相似度
double w = 0;
foreach (var mid in backuser[fid].Keys) w += backuser[fid][mid] * classtypeweight[mid];
// 将加权后的相似度添加到用户的相似用户字典中
users[id1].SimFusers.Add(fid, w);
}
}
// 这段代码主要在计算用户id1和他的相似用户们之间的预测值,并根据不同的情况将这个预测值和相关数据放入不同的字典中。
// 这个预测值的计算是基于各种不同的因素,包括用户的信息,用户和他的相似用户的关系,以及用户的行为等等。
// 这些因素通过一系列复杂的计算和调整,被整合到一个预测值中,以进行后续的预测任务
// 这个预测策略是基于用户的行为数据,和用户之间的关系网络,以及对数据的一些基本假设
// (比如一些数值通过指数函数转换,以减小数值的影响)来进行的。
foreach (var fid in users[id1].SimLusers.Keys)
{
// 开始遍历用户id1的相似用户列表
// 初始化一些变量
double r = 0, sir = 1, fir = 1, kir = 0, expN = 0.1;
if (dataLF.ContainsKey(id1) && dataLF[id1].ContainsKey(fid))
// 如果dataLF字典中存在以id1为键的键值对,且以fid为键的键值对,进行下一步操作
foreach (var ssitem in dataLF[id1][fid])
// 对于dataLF[id1][fid]中的每个项目(ssitem),进行下面的操作
if (dataItemLF.ContainsKey(fid) && dataItemLF[fid].ContainsKey(ssitem))
// 如果dataItemLF中存在以fid为键的键值对,并且以ssitem为键的键值对,进行下面的操作
kir += dataItemLF[fid][ssitem].Count; // 计算每一个项目的二次转发次数
kir = Math.Exp(0.01 * kir); // 对kir应用指数函数进行转换,使得大的kir值不会对后续计算产生过大影响
if (users[fid].Ratio[0].ContainsKey(itemsinfo[itemid].CateLevelOneId))
// 如果用户fid的Ratio的第一项包含itemsinfo中的项目的CateLevelOneId,进行下面的操作