1.トピックとコンテンツ
ビッグデータ分析の問題
【問題内容】
某搜索公司一天的用户搜索词汇是海量的(百亿数据量),请设计一种求出每天最热top 100 词汇的可行办法。
【基本要件】
(1)大量データはランダムに生成され、ファイルに保存されます。データはファイルから読み込まれて処理されます。
(2)データファイルの各処理の結果を表示します。
2.思考プロセス
(1)生成随机大数据,并且要求大数据存在交集数据,以此保证有热词存在。
(2)顺序读大文件,对于每一行,也就是每个词x,取hash(x)%n,
然后按照该值存到n个小文件中(即为1,2,3,4…..n),这样将MB级别,或者GB级别大文件分块成kb级别,
如果有的文件超过了1M大小,还可以按照类似的方法继续往下分,直到分解得到的小文件的大小都不超过1M;
(3)读入每一个小文件,利用hash_map统计每个文件中出现的词以及相应的频率,
创建动态数据结构node组,存储data(键值)和num(频率),利用最大堆排列,并取出出现频率最大的100个词,
这里注意不一定只有100词,需要统计100之后是否存在和100相同的值,并把100词和相应的频率以结构[key,num]存入文件中,这样又得到n个文件。
(4)把n个文件读入归并成1个文件all.txt, 创建动态数据结构node组,存储data(键值)和num(频率),
利用最大堆排列,取出出现频率最大的100个词,思路到此。
3.プログラムのフローチャートと注釈付きプログラム
1.ビッグデータファイルを生成する
ここで使用したpythonによって生成されたデータは、ローカルコンピューターの負担を軽減するために、サーバー上で直接生成したかったためです。最終的に生成されるデータは15503190個のデータで約1500万、スペースは81MBです。この数は数百億レベルには達していませんが、理論的には数百億のデータは同じ考えです。英語が中国語で使われない理由は、最初はランダムな中国語の文字を使用しているが、中国語の文字の交差が非常に小さいため、熱い言葉を出すのが難しいからです。
プロセス:
ループ(1.5k)==ランダムなソースを設定->マージする長さのランダムな単語を選択->ファイルを保存
Pythonコード:#新しいデータの挿入に使用
1. def create():
2. filepath = 'data.txt'
3. dataFrom = ['a','b','c','d','e','f','g','h','i','j'] # 随机来源
4. for i in range(10000000):
5. if(i%10000 == 0):
6. print(i)
7.
8. data = ''
9.
10. for j in range(random.randint(2,5)):
11. data += random.choice(dataFrom) # 产生2到5的长度数据
12.
13. with open(filepath , 'a' ) as f: # 存储文件
14. f.write(data + '\n')
15. f.close()
ps:レポートを書いた後、openではサイクルごとに実行する必要がないことがわかりました。。。グローバルになるように変更できます!
2.大きなファイルを順番に読み取る
ここではC ++が使用されます。各行、つまり各単語xについて、hash(x)%nを取得し、その値をn個の小さなファイル(つまり、1、2、3、4 ... n)に格納します。このように、MBレベルまたはGBレベルの大きなファイルはkbレベルに分割され、私が取得した最大の小さなファイルは609kbです。
プロセス:
ブロックサイズを設定しますnum->大きなファイルを読み取りますall.txt->
ループ(= true)==各行のハッシュを計算します%num =ブロックの場所->対応するブロック領域を保存します
#include <iostream>
2. #include <fstream>
3. #include <string>
4. #include <typeinfo>
5. #include <iomanip>
6.
7. using namespace std;
8.
9.
10. int spit(int num = 500) {
11.
12. hash<string> str_hash; // 哈希函数
13.
14. ifstream file;// 大数据来源
15. file.open("C:\\Users\\lenovo\\Desktop\\大二上作业\\数据结构\\data.txt",
ios::in);
16.
17. if (!file.is_open())
18.
19. return 0;
20.
21.
22. std::string strLine;
23.
24. int i = 0;
25.
26. ofstream *ofile = new ofstream[num+1]; // 预处理存储num个小文件,num可以自定义,这里设置500
27.
28. for (i = 1; i <= num; i++) { // 目的是open只需要1次,而不是每一次写入都要open,浪费资源
29. string filename = to_string(i) + ".txt"; // 存储名
30. ofile[i].open("C:\\Users\\lenovo\\Desktop\\大二上作业\\数据结构\\ton\\" +
filename);
31. }
32.
33. i = 0;
34.
35. while (getline(file, strLine))// 读入大数据文件每一行
36. {
37. i++;
38.
39. if (strLine.empty()) // 是否为空
40. continue;
41.
42. int ton = str_hash(strLine) % num; // 哈希计算分块位置
43.
44. ofile[ton] << strLine << endl; // 写入一行
45.
46. if (i % 10000 == 0) {
47. cout << i << endl; // 每写入10000行输出一次,进度说明
48. }
49.
50. }
51.
52. file.close();
53. for (i = 1; i <= num; i++) {
54. ofile[i].close();
55. }
56. delete[] ofile;
57. }
3.すべての小さなファイルを読み込みます
hash_mapを使用して、各ファイルに表示される単語と対応する頻度をカウントし
、動的データ構造ノードグループを作成し、データ(キー値)とnum(頻度)を格納し、最大のヒープ配置を使用して、最も頻度の高い100個の単語を取り出します。ここに注意してください。必ずしも100ワードである必要はありません。100の後に100と同じ値があるかどうかを数え、100ワードとそれに対応する頻度を構造[key、num]のファイルに格納して、n個のファイルを取得する必要があります。
これが中心的な場所です。
プロセス:
loop(500)==
小さなファイルを読み取る-> hash_mapを使用して頻度をカウントする->
hash_mapを
使用してノード構造のツリーグループに移動する->最大のヒープを使用してツリー構造を並べ替える->
上位100個のデータをファイルに出力する
1. int get100() {
2.
3. for (int q = 1; q <= 500; q++) {
4. hash_map<string, int> hm; //建立hash_map 结构 以数据作为key,频率作为value
5. hash_map<string, int> ::iterator it;
6.
7. ifstream file; // 建立小文件对象
8. ofstream ofile; // 建立小文件对象
9.
10. string filename = to_string(q) + ".txt"; // 文件来向和去向设定
11. file.open("C:\\Users\\lenovo\\Desktop\\大二上作业\\数据结构\\ton\\" + filename, ios::in);
12. ofile.open("C:\\Users\\lenovo\\Desktop\\大二上作业\\数据结构\\100\\" + filename);
13.
14. if (!file.is_open())
15. break;
16.
17. string strLine;
18. int allNum = 0; // 统计当前小文件的行数,便于创建动态结构node组
19. while (getline(file, strLine)) {
20. if (strLine.empty())
21. continue;
22. hm[strLine] += 1; // hash_map 统计频率
23. allNum++;
24. }
25. cout << "*****" << endl;
26.
27. it = hm.begin();
28.
29. node* tree = new node[allNum]; // 用来排序
30. int startNum = 0;
31.
32. while (it != hm.end()) // 遍历hash_map
33. {
34. tree[startNum].num = it->second; //存储数据
35. tree[startNum].data = it->first;
36. startNum++;
37. // cout << "pair it_map.key = " << it->first << " pair it_map.string = " << it->second << endl;
38. ++it;
39. }
40.
41. Heap_sort(tree, allNum);// 最大堆排列,传入的是node结构
42. cout << "***" << endl;
43.
44. int i = 0;
45. while (true) // 输出前一百的数据
46. {
47.
48. i++;
49. cout << i << " " << tree[i].num << " " << tree[i].data << endl;
50. ofile << tree[i].data <<","<< tree[i].num << endl;
51.
52. if (i >= 100 && tree[i].num != tree[i + 1].num) // 保证100之后是否存在和100相同的数据
53. break;
54.
55.
56.
57. }
58. delete[] tree; // 释放空间
59. file.close();
60. ofile.close();
61.
62. }
63.
64. return 0;
65. }
4.n個のファイルを読み取って1つのファイルall.txtにマージします。
プロセス:
loop(500)==同じall.txtを書き込む->行数を数えて戻ります。
1. int getALl(int &num) {
2.
3. ofstream ofile;
4. ofile.open("C:\\Users\\lenovo\\Desktop\\大二上作业\\数据结构\\all.txt"); //来源
5.
6. for (int q = 1; q <= 500; q++) {
7. ifstream file;
8.
9.
10. string filename = to_string(q) + ".txt"; //去向
11. file.open("C:\\Users\\lenovo\\Desktop\\大二上作业\\数据结构\\100\\" + filename, ios::in);
12.
13. if (!file.is_open())
14. break;
15.
16. string strLine;
17.
18. while (getline(file, strLine)) {
19. if (strLine.empty())
20. continue;
21. ofile << strLine << endl;
22. num += 1;
23.
24. }
25.
26. file.close();
27.
28. }
29.
30. return 0;
31. }
5.動的データ構造ノードグループを作成します。
データ(キー値)とnum(頻度)を保存し、最大ヒープ順列を使用して、最も頻度の高い100ワードを取り出します。それだけです。
上位100件のデータをファイルに出力する
1. int main() {
2.
3. int dataLine;
4. getALl(dataLine);
5.
6. cout << dataLine;
7.
8. ifstream file;
9. file.open("C:\\Users\\lenovo\\Desktop\\大二上作业\\数据结构\\all.txt", ios::in);
10. ofstream ofile;
11. ofile.open("C:\\Users\\lenovo\\Desktop\\大二上作业\\数据结构\\last.txt");
12.
13. node* tree = new node[dataLine];// 动态结构node
14. int startNum = 0;
15.
16. string strLine;
17.
18. while (getline(file, strLine)) {
19. if (strLine.empty())
20. continue;
21.
22. vector<string> v = split(strLine , ","); //分割split;
23. tree[startNum].num = to_int(v[1]);
24. tree[startNum].data = v[0];
25. startNum++;
26.
27. }
28.
29.
30. Heap_sort(tree, dataLine);
31. cout << "***" << endl;
32.
33. int i = 0;
34. while (true)
35. {
36. i++;
37. cout << i << " " << tree[i].num << " " << tree[i].data << endl;
38. ofile << i << " " << tree[i].num << " " << tree[i].data << endl;
39.
40. if (i >= 100 && tree[i].num != tree[i + 1].num) // 输出判断前一百
41. break;
42. }
43. delete[] tree;
44. file.close();
45. ofile.close();
46.
47.
48. }
4.プログラム名を実行し、プログラム実行時の初期値と動作結果を出力します。
1.ビッグデータを生成し、サーバーでpythonを使用します
ファイルの生成:
2.500ファイルをブロックします
ソースファイルは85MBで、ブロックメモリは4MBを使用します
3.各小さなファイルを読み込み、hash_mapを使用して各ファイルに表示される単語と対応する頻度をカウントし、動的データ構造ノードグループを作成し、データ(キー値)とnum(頻度)を格納し、最大のヒープ配置を使用して、削除します。最も頻繁に使用される100の単語。
4.最後に、最初の100世代
5.実験結果、実験的利益および経験の分析
1.このビッグデータの問題は、実際には非常に重要です。読み取りに1Gのメモリを必要とする10Gファイルの場合、メモリ要件は非常に高く、重要なアイデアは、分割して征服し、処理をブロックすることです。!!
2.最初はノードグループを作成し、削除処理を使用しなかったため、メモリフットプリントが約1Gに急増しましたが、削除後は4MB未満しか占有しません。これは、コードがリソースを非常によく把握しており、リソース占有の問題に対処する方法が非常に重要であることを反映しています。
3. hash_mapがそれを使用する理由は、クエリが非常に高速で、時間の複雑さが非常に低く、バケット分散の原則と同様に、そのアイデアも分割して征服するためです。
6.実験の改善のための提案と提案。
ここでの実験は、分割して征服するという考えに基づいていますが、ローカルマシンで実行する必要はありません。実際、データが特定のレベルに達すると、分散コンピューティングを使用してデータを異なるサブマシンに分割でき、サブマシンは処理を中央マシンに送信します。パラメータを同期した後、それを異なるサブマシンに分割し続け、次にループしてデータの統計を実現します。
また、データ量はまだ大きな問題ではないため、バケットの分散は500です。データが多い場合は、より多くのバケットを分割できます。
さらに、実際には、1500万の処理、プロセス全体を完了するのに約290秒かかります。私のデバイスi5は3.7Ghz、メモリ20Gを実行し、マシンごとに時間が異なる場合があり、主な時間は行データの読み取りに費やされます。マルチスレッド処理メカニズムやメモリマッピングなどを使用できるかどうかを考えました。後で、メモリの少ない大きなデータを処理する問題について考えました。マルチスレッド処理の場合は意味がありません。上記の分散処理を使用することをお勧めします。