一.题目及内容

大数据分析问题

[问题描述]
某搜索公司一天的用户搜索词汇是海量的(百亿数据量),请设计一种求出每天最热top 100 词汇的可行办法。
[基本要求]

(1) 随机生成海量数据,存入文件;从文件读入数据来处理。
(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个词,思路到此。

三. 程序流程图和带有注释的程序

1.生成大数据文件

这里我使用的python生成的数据,之所以用python是当时我想直接在服务器上生成,减少本地电脑的负担。最后生成的数据是15003190条数据,也就是1500万左右,空间81MB,虽然这个数字达不到百亿级别,但是理论上百亿数据是一样的思路。之所以用英文不用中文是由于最开始使用的是随机汉字,但是汉字交集很小,很难有热词。

流程:
循环(1.5k)== 设定随机来源- > 选取长度随机词合并 -> 存储文件
Python代码:# which used for inserting new data

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:事后写报告发现这里with open没必要每个循环都执行。。。可以修改为全局性!

2.顺序读大文件

这里用的是c++,对于每一行,也就是每个词x,取hash(x)%n,然后按照该值存到n个小文件中(即为1,2,3,4……n),这样将MB级别,或者GB级别大文件分块成kb级别,最后我得到的小文件最大的占用就是609kb。

流程:
设定分块大小num –> 读入大文件all.txt -->
循环(=真) ==对每一行哈希计算%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统计每个文件中出现的词以及相应的频率
创建动态数据结构node组,存储data(键值)和num(频率),利用最大堆排列,并取出出现频率最大的100个词,这里注意不一定只有100词,需要统计100之后是否存在和100相同的值,并把100词和相应的频率以结构[key,num]存入文件中,这样又得到n个文件。

这里是最核心的地方。

流程:
循环(500)==
读入小文件 - > 利用hash_map统计频率 ->
hash_map 遍历到node结构的tree组 ->
利用最大堆对tree结构排序 - >
输出前一百的数据到文件

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。

流程:
循环(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.创建动态数据结构node组,

存储data(键值)和num(频率),利用最大堆排列,取出出现频率最大的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.	}

四.执行程序名,并打印程序运行时的初值和运算结果

1.生成大数据,在服务器用python生成

数据分析对电脑内存要求_大数据


生成文件:

数据分析对电脑内存要求_数据结构_02

2.分块500文件

数据分析对电脑内存要求_数据分析对电脑内存要求_03

源文件85MB,分块内存占用4MB

数据分析对电脑内存要求_数据分析对电脑内存要求_04


数据分析对电脑内存要求_分块_05

3. 读入每一个小文件,利用hash_map统计每个文件中出现的词以及相应的频率,创建动态数据结构node组,存储data(键值)和num(频率),利用最大堆排列,并取出出现频率最大的100个词。

数据分析对电脑内存要求_分块_06


数据分析对电脑内存要求_数据_07


数据分析对电脑内存要求_数据_08

4.最后,前一百生成

数据分析对电脑内存要求_数据_09

五.实验结果分析,实验收获和体会

1.这道题大数据这一点其实体现的很关键,如果是10G的文件,要求1G内存读取,那么就对内存要求非常高了,而关键的思想就是分而治之,分块处理!!!

2.最开始我创建node组没有用delete处理导致内存占用飙升到1G左右,而delete后占用不到4MB,体现了代码对资源的把握要非常高,如何处理资源占用问题非常关键。

3.hash_map之所以使用它,也是由于它的查询非常快,时间复杂度非常低,并且它的思想也是分而治之,类似于桶分布原理。

六.实验的改进意见和建议。

这里的实验我采用的是分而治之思想,但是不一定要在本机运行,当数据达到一定程度,其实可以用分布式计算,把数据划分给不同的子机器,子机器处理后传达给中心机器,同步参数后继续划分给不同的子机器,依次循环即可实现对数据的统计。

并且这里因为数据量还是不大的问题,所以桶分布为500,如果数据更多可以划分更多的桶

再则,其实1500万处理,完成 整个过程也要大概290多秒的时间,本人设备 i5 均跑3.7Ghz,内存 20G,不同机器可能时间不同,主要时间还是花费在读取行数据上面。想过是否能采用多线程处理机制,或者说内存映射之类,后来想想题目根本点在于低内存处理大数据,如果多线程处理,就失去意义了,倒不如采用上面分布式处理!