Analysis for INFO 212 Data Science Programming I Project

数据集

数据集由三部分组成:样本数据集samples,设备数据集devices和app处理数据集app_processes

题目描述

此次的题目是从3个组里面选择2个组,6个小题里面选择4个小题,来进行作答
英文的话是这样

Group 1

1.1. Determine the ten brands of devices that, according to the Greenhub dataset, appear in more samples. Do the same for device models. What are the most prevalent models in the dataset, in terms of presence in the samples? Are there cases of brands that appear in the list under different names?

1.2. Create two pie charts, one showing the slices corresponding to the ten brands and one chart where the slices correspond to the proportions for the ten models. Include in each pie chart an additional slice corresponding to the other brands and models. The radius of this other slice should, of course, be representative of the percentage of the samples that corresponds to the devices of other models and brands.

Group 2

2.1. Determine the ten apps that are not system apps (is_system_app == 1) that appear more often according to the dataset. Use the name of the apps but, when reporting the results, present both the name and the application_label.

2.2. Establish the twenty smartphone brands with the most models in the dataset. From those, select all the brands that are Chinese and those that are South Korean. Now contrast the apps that appear more often in samples associated with those brands (similarly to the previous task, but constrained to these brands) with the most popular apps running on devices of South Korean brands.

Group 3

3.1. When studying the energy impact of different apps, we are usually interested in discharge sequences. A discharge sequence is a sequence of samples where the device_id is the same, timestamps are contiguous, and every sample in the sequence has battery_state == 'Discharging'. You can identify the beginning of a discharge sequences by identifying moments (between two consecutive samples for the same device_id) when battery_state transitioned from any other state (Full, Not Charging, or Charging) to Discharging and the end of a sequence when consecutive samples for the same device_id transition from Discharging to any other state. Build code to organize the samples in dataset-samples so as to associate discharge sequence codes (you’re responsible for creating a sequence of them) to the ids of the samples in the corresponding discharge sequence. What is the length of the longest discharge sequence, in terms of the difference between the timestamp of the last sample in the sequence and the timestamp of the first one.

3.2. Determine the 10 apps (again, not system apps) that appear more often in discharge sequences.

初步的话,看着组1、2是比较简单的,就选这俩了

第1组

1.1 根据Greenhub数据集,确定在所有样本中出现次数最多的设备的10brands。对设备models执行相同的操作。就样本中的存在而言,数据集中最流行的model是什么?列表中是否有使用不同名称出现的品牌案例?

1.2 创建两个饼图,一个饼图显示与十个brands相对应的切片,而另一个饼图与十个models的比例相对应。在每个饼图中都包含一个与其他品牌和型号相对应的附加切片。当然,此其他切片的半径应代表与其他型号和品牌的设备相对应的样本百分比。

第2组

2.1 根据数据集确定出现频率更高(最高)的十个非系统应用is_system_app == 1)。将应用程序的name与频率结合,但是在报告(在report上展示)结果时,要同时显示nameapplication_label

根据数据集的确定name出现频率更高(最高)的十个非系统应用is_system_app == 0)

2.2 建立数据集中models最多的20个智能手机brands。从这些中,选择所有Chinese brandsSouth Korean brands。现在,将与这些brands相关的samples中出现频率更高的apps(与之前的任务类似,但仅限于这些brands)与在South Korean brands的设备上运行的最受欢迎的apps进行对比。

题目分析

GROUP1

1.1

题目描述的不是很清晰,说实在的,有点歧义存在的。在devices数据集里面,对于每一个安卓手机都有其对应的id以及对应的信息:model;manufacturer;brand;os_version;is_root;created_at

但是,他说

Determine the ten brands of devices that, according to the Greenhub dataset, appear in more samples.

这个品牌在越多的样本中出现,那么它就越会成为这top10中的一员,那么这个samples样本指的是什么?是仅仅统计各个参与Greenhub统计的手机信息中的品牌,还是统计在samples或(逻辑或)在app_processes数据集中含有该品牌数据字段的数量呢?

往简单的方面想,仅仅是统计前者吧,如果是后者,需要将对应id映射到brand,而且要读取异常大的数据量。

考虑到他在说明文档最后对report撰写要求中提到的:

An explanation about how the team scaled up the solution so as to deal with the large size of the dataset, specially for groups 2 and 3.

他要做题者解释一下如何处理大数据,特别是对GROUP2和3,在这儿他没有提起GROUP1,那么题目1.1的解法极大概率是分析的前者情况了。

但是,后来一想,存在这个samples数据集,而且数据量为165个文件,并不是非常大,而且如果仅仅分析前者非常简单,不像是团队项目要完成的任务量。

综上所述,要对每一个id对应的brand映射好,构造brand字典,遍历samples数据,将数据统计到brand字典当中。

Are there cases of brands that appear in the list under different names?

后来外教又声明了一下:

About the question “Are there cases of brands that appear in the list under different names?”, what I want to know is if there are smartphone manufacturers in the dataset that appear with similar but different names? If not, tell me so. If yes, which ones?

这句话就是说有没有手机制造商相似(同)但是名字却不同?

我认为这句话就是说,是否同一个品牌以不同的名字出现。存在。因为有HUAWEI和Huawei,这个可以用统一大写和不进行操作得到,操作之后brand变多了!这个几乎和2.2的第一小问解法一样。

1.2

要做两个饼图,
饼图1:10个top10以及1个other的brands(的比例)信息;
饼图2:10个top10以及1个other的models(的比例)信息。

其中对于slice的解释,在WordNet上搜索,

piece, slice – (a serving that has been cut from a larger portion; “a piece of pie”; “a slice of bread”)

这里的slice解释可以为扇区,就是饼图的一块,一部分。

其中最难理解的是对radius的解释,按常理这应该翻译为半径,但是饼图不是按照面积大小来反映比例吗?怎么成半径了?难不成是玫瑰图?在网上搜了,玫瑰图是饼图的变种图,所以说可以说玫瑰图是饼图。在知乎上搜了搜:

https://zhuanlan.zhihu.com/p/345262150
Analysis for INFO 212 Data Science Programming I Project_第1张图片

起初还想radius是不是弧度,如果是弧度的话就是纯饼图了,但是弧度的英文是radian,那么此题就是要画玫瑰图,形状为上图的左上图。

由于涉及到了其他项,把数据总量当成分母进行运算即可。

GROUP1

2.1

数据处理是针对app_processes数据集。

根据布尔表达式is_system_app == 1来确定是否保留字段,字段属性值仅保留nameapplication_label,根据个人猜测,nameapplication_label是一一对应的,那么,如要减少内存占用量,可先将映射关系保存在字典中。

但是nameapplication_label不是一一对应的,比如说存在:

com.google.android.music:main;Google Play Music
com.google.android.music:main;Google Play Música

也就是说一个namecom.google.android.music:main对应了多于一个application_labelGoogle Play MusicGoogle Play Música

2.2

又是意义不明确的一句:

Establish the twenty smartphone brands with the most models in the dataset.

the dataset到底是哪个数据集呀?这儿个人的分析是devices数据集,就是仅仅用该数据集创造一个{brands:num of models}的字典,字典的键是智能手机品牌,值是该品牌拥有的不同型号(因为存在一个品牌的同一型号出现多次)数量。

其次,要构造出{brands:countries}的字典,但是搜了三个数据集的属性值,仅有的是samples数据集中的timezonecountry_code属性,其中存在这样的值:

America/Chicago;us
Europe/Lisbon;pt

但是你想,这是时区和其对应的国家代码,有可能中国生产的手机但是美国人在用啊!所以我认为品牌是中国还是韩国的还是要在网络上进行搜索并构造出映射表。因为仅仅有top20的品牌,所以比较容易遍历完。

做题

2.1

对于将两个字典进行合并是很重要的,而且不光是要求并集,而且要把相同的数值进行相加处理:

def sum_dict(a,b):
    temp = dict()
    for key in a.keys()| b.keys():
        temp[key] = sum([d.get(key, 0) for d in (a, b)])
    return temp

2.2.1

path = '../dataset-devices/devices.query.1.csv'
df = pd.read_csv(path,sep=';')
df.head()

可见
Analysis for INFO 212 Data Science Programming I Project_第2张图片
思路是:把brand和model抽取出来,进行去重处理,统计每个brand的数量就是每个brand所拥有的model数量。

于是构造出一个函数:

def get_top20_brand_model_type_counts():
    path = '../dataset-devices/'
    frames=[]
    for file in os.listdir(path):
        path_f = path+file
        df = pd.read_csv(path_f,sep=';')
        data = df.loc[:,['model','brand']]
        frames.append(data)
    data = pd.concat(frames)
    return data.drop_duplicates().brand.value_counts()[:20]

查看一下结果:

get_top20_brand_model_type_counts()
samsung     1321
lge          534
alps         496
TCL          418
HUAWEI       378
ZTE          286
Lenovo       268
htc          195
Sony         193
TECNO        188
asus         187
Micromax     186
OPPO         179
BLU          173
QMobile      154
motorola     153
Huawei       152
vivo         143
Android      141
ADVAN        121
Name: brand, dtype: int64

可以看到HUAWEI和Huawei是同一个品牌啊,但是他们却分成了不一样的类别,所以应该提前进行数据清洗,把所有的改成大写或者改成小写。

在进行去重之前,首先对model和brand列进行全大写归一化处理:

    data.model=data.model.apply(lambda x:str(x).upper())
    data.brand=data.brand.apply(lambda x:str(x).upper())

之后运算的结果为:

SAMSUNG     1392
LGE          533
HUAWEI       531
ALPS         495
TCL          418
ZTE          302
LENOVO       269
TECNO        220
HTC          200
SONY         193
MICROMAX     190
ASUS         189
OPPO         179
BLU          175
QMOBILE      164
MOTOROLA     156
LAVA         156
ANDROID      149
VIVO         144
INTEX        138
Name: brand, dtype: int64

其实这样算出来是错误的,三十多万个数据怎么这么少?在写其他代码的时候发现了这一点,就是索引不一致的问题,在concat合并的时候一定要:

    data = pd.concat(frames).reset_index()

其中还有个drop参数。

最后的结果是这样的:

SAMSUNG     84342
XIAOMI      28558
OPPO        22095
VIVO        15454
HUAWEI      14709
MOTOROLA    12741
LGE         10909
LENOVO       8745
ASUS         5533
TCL          5189
TECNO        5113
ITEL         4572
MICROMAX     4227
ZTE          3659
NOKIA        3639
ADVAN        3571
LAVA         3537
INFINIX      3033
HONOR        2760
SONY         2712
Name: brand, dtype: int64

其实这样也是错误的,原因:
Analysis for INFO 212 Data Science Programming I Project_第3张图片
虽然reset了index,但是index这一列没有删除,这就导致去重无效。

SAMSUNG     1392
LGE          533
HUAWEI       531
ALPS         495
TCL          418
ZTE          302
LENOVO       269
TECNO        220
HTC          200
SONY         193
MICROMAX     190
ASUS         189
OPPO         179
BLU          175
QMOBILE      164
LAVA         156
MOTOROLA     156
ANDROID      149
VIVO         144
INTEX        138
Name: brand, dtype: int64

所以说结果是没错的。

还有一点,有着HUAWEI.这样的brand,可以用如下函数对比发现:

# only keep upper characters and numbers
def drop_duplicate(x):
    temp=''
    for i in x:
        if 48<=ord(i)<=57 or 65<=ord(i)<=90:
            temp+=i
    return temp

再对brand操作一下:

	data.brand=data.brand.apply(drop_duplicate)

得到的结果:

SAMSUNG     1392
LGE          533
HUAWEI       532 <- 531
ALPS         495
TCL          418
ZTE          302
LENOVO       269
TECNO        220
HTC          200
SONY         194 <- 193
MICROMAX     190
ASUS         189
OPPO         179
BLU          175
QMOBILE      168 <- 164
MOTOROLA     156
LAVA         156
ANDROID      149
VIVO         144
INTEX        138
Name: brand, dtype: int64

2.2.1的完整代码如下:

import pandas as pd 
import os 

def get_top20_brand_model_type_counts():
    path = '../dataset-devices/'
    frames=[]
    for file in os.listdir(path):
        path_f = path+file
        df = pd.read_csv(path_f,sep=';')
        data = df.loc[:,['model','brand']]
        frames.append(data)
    data = pd.concat(frames).reset_index()
    data.model=data.model.apply(lambda x:str(x).upper())
    data.brand=data.brand.apply(lambda x:str(x).upper())
    
    return data.drop_duplicates().brand.value_counts()[:20]

经过在网络上查找,可以获取到品牌对应的国家:

southKorean=['SAMSUNG','LGE']
Chinese=['XIAOMI','OPPO','VIVO','HUAWEI','LENOVO','ASUS','TCL','TECNO','ITEL','ZTE','INFINIX','HONOR']

经过更改,是这样的:

Korean=['SAMSUNG','LGE']
Chinese=['HUAWEI','TCL','ZTE','LENOVO','TECNO','HTC','ASUS','OPPO','VIVO']

2.2.2

要构造出来一个手机id与生产国家对应的,如1:‘China’,3:‘southKorean’,这样的;然后再在app_processes里面进行遍历与统计,类似于2.1。

samples里面的id对应app_processes里面的sample_id
samples里面的device_id对应devices里面的id
想要知道app_processes里面的sample_id对应什么device_id,从而知道是什么品牌
app_processes里面有id;sample_id,id用不着,找sample_id到device_id的映射
就是找samples里面的id到他的device_id的映射
取smaples里的id和device_id这两列再搞映射关系即可

首先,要在devices-dataset里面获取到device_id和国家名的映射关系,因为samples-dataset里面有id和device_id,所以根据上面获得的映射关系,将device_id映射到国家,可以获得id和国家的映射关系,也就是sample_id和国家的映射关系。
之后遍历app_processes-dataset中的所有文件,首先筛选出非系统文件,取出sample_id列和name列,将之前的sample_id-country映射字典应用在sample_id列上,再以中国和韩国为不同的统计对象进行应用名的统计即可,且在文件的不断访问中不断拼接统计的数据。
最后得到的数据分别是中国、韩国的前十个出现次数最多的非系统应用,对于相同的应用名,采用饼图进行对比。

def get_sampleid_deviceid_map():
    path = '../samples/'
    res_dict = {}
    for file in os.listdir(path):
        path_f = path+file
        df = pd.read_csv(path_f,sep=';')      
        res_dict = {**res_dict,**dict(df.loc[:,['id','device_id']].values)}
    return res_dict

Analysis for INFO 212 Data Science Programming I Project_第4张图片
在这里插入图片描述
这个文件太大了,而且没有必要把所有的映射都弄上去,只弄韩国和中国的就行了,要不然非常大,而且非常耗时,这需要结合布尔表达式在每次处理文件的时候筛掉。

所以进行了精简

%%time
southKorean=['SAMSUNG','LGE']
Chinese=['XIAOMI','OPPO','VIVO','HUAWEI','LENOVO','ASUS','TCL','TECNO','ITEL','ZTE','INFINIX','HONOR']
# process daevices
path = '../dataset-devices/'
frames=[]
for file in os.listdir(path):
    path_f = path+file
    df = pd.read_csv(path_f,sep=';')
    data = df.loc[:,['id','brand']]
    frames.append(data)
data = pd.concat(frames).reset_index(drop=True)
data.brand=data.brand.apply(lambda x:str(x).upper())
deviceid_country_map=dict(zip(data.id.values,data.brand.apply(conf_country).values))
# process samples
path2 = '../samples/'
res_dict = {}
for file in os.listdir(path2)[:20]:
    path_f2 = path2+file
    df2 = pd.read_csv(path_f2,sep=';')
    df2=df2.loc[:,['id','device_id']]
    df2.device_id=df2.device_id.map(deviceid_country_map)
    processed_dict=dict(zip(df2.dropna().id,df2.dropna().device_id))
    res_dict = {**res_dict,**processed_dict}

读取完20个文件需要29.5 s,而且用

float(sys.getsizeof(res_dict))/(2**20)

计算得到的字典的内存占用量仅为160MB,那么再测试一下全部165个文件的情况。

全部文件用时5min 30s,占用1280MB、1.25GB内存,可以接受。
Analysis for INFO 212 Data Science Programming I Project_第5张图片
这几乎和之前得到的相差了20000000个数值。

Analysis for INFO 212 Data Science Programming I Project_第6张图片
但是结果仍然很离谱,如果按照这个速度仅仅进行映射,需要跑27个小时才能跑完…

令人非常兴奋的是,我找到了一个解法!这么长的时间,一猜就是进行了遍历,就是说,要遍历完字典中所有的值才会结束。但是直接在字典中索引一个值是不会消耗很长的时间的,所以:

def deom(x):
    try:
        return res_dict[x]
    except KeyError:
        return None
path6='../app_processes/app_processes.query.1.csv'
df6=pd.read_csv(path6,sep=';')
df7=df6[df6.is_system_app==0]
df7.sample_id.apply(deom)

对比一下:
Analysis for INFO 212 Data Science Programming I Project_第7张图片Analysis for INFO 212 Data Science Programming I Project_第8张图片
这是何等的差距!

df8=df7.copy()
df8.sample_id=df8.sample_id.apply(deom)
df8[~df8.sample_id.isna()]

Analysis for INFO 212 Data Science Programming I Project_第9张图片
在测试中出现了一个报错:

A value is trying to be set on a cop`在这里插入代码片`y of a slice from a DataFrame

这个处理方式是用data2=data1.copy(),然后操作data2。

如下代码是获取映射字典的函数:

def conf_country(x):
    if x in southKorean:
        return 'Korean'
    elif x in Chinese:
        return 'Chinese'
    else:
        return None
# 获取sample_id,country映射字典:
def get_sampleid_country_map():
    southKorean=['SAMSUNG','LGE']
    Chinese=['XIAOMI','OPPO','VIVO','HUAWEI','LENOVO','ASUS','TCL','TECNO','ITEL','ZTE','INFINIX','HONOR']
    # process daevices
    path = '../dataset-devices/'
    frames=[]
    for file in os.listdir(path):
        path_f = path+file
        df = pd.read_csv(path_f,sep=';')
        data = df.loc[:,['id','brand']]
        frames.append(data)
    data = pd.concat(frames).reset_index(drop=True)
    data.brand=data.brand.apply(lambda x:str(x).upper())
    deviceid_country_map=dict(zip(data.id.values,data.brand.apply(conf_country).values))
    # process samples
    path2 = '../samples/'
    res_dict = {}
    for file in os.listdir(path2):
        path_f2 = path2+file
        df2 = pd.read_csv(path_f2,sep=';')
        df2=df2.loc[:,['id','device_id']]
        df2.device_id=df2.device_id.map(deviceid_country_map)
        processed_dict=dict(zip(df2.dropna().id,df2.dropna().device_id))
        res_dict = {**res_dict,**processed_dict}
    return res_dict

以下是完整的操作中国和韩国品牌对应的非系统应用使用频率情况:

def sum_dict(a,b):
    temp = dict()
    for key in a.keys()| b.keys():
        temp[key] = sum([d.get(key, 0) for d in (a, b)])
    return temp

# 在使用之前要注意res_dict是否被定义,res_dict=get_sampleid_country_map()
def map_func(x):
    try:
        return res_dict[x]
    except KeyError:
        return None

def app_name_CN_KR(start=1,num=10,allData=False):
    path = '../app_processes/'
    all_path_list = [path+i for i in os.listdir(path)]
    ChinaDict = {}
    KoreanDict = {}
    for file in all_path_list if allData==True else all_path_list[start-1:start-1+num]:
        df = pd.read_csv(file,sep=';',error_bad_lines=False, warn_bad_lines=False)
        data = df[df.is_system_app==0]
        data2=data.copy()
        data2.sample_id=data2.sample_id.apply(map_func)
        data2=data2[~data2.sample_id.isna()]
        
        ChinaDict = sum_dict(ChinaDict,dict(data2[data2.sample_id=='Chinese'].name.value_counts()))
        KoreanDict = sum_dict(KoreanDict,dict(data2[data2.sample_id=='Korean'].name.value_counts()))
        ChinaDict = dict(sorted(ChinaDict.items(),key=lambda x:x[1],reverse=True))
        KoreanDict = dict(sorted(KoreanDict.items(),key=lambda x:x[1],reverse=True))
        
    return (list(ChinaDict)[:10], list(KoreanDict)[:10])

Analysis for INFO 212 Data Science Programming I Project_第10张图片
操作100个文件需要32.2s,粗略计算,5912个文件,总共32.2*(5912/100)=1903.664s,31.73min。

又从第3000个文件往后读取了200个文件:
Analysis for INFO 212 Data Science Programming I Project_第11张图片
其实如果代码是正确的,在jupyter notebook上可以忽略提示,在网上查找是使用如下代码:

import warnings
warnings.filterwarnings('ignore')

但是没用。

还有人说可以这样用:

pd.option.mode.chained_assignment = None

但是:

module 'pandas' has no attribute 'option'

经过查找发现read_csv里面还有一个参数可以使用:

data = pd.read_csv(path, error_bad_lines=False, warn_bad_lines=False)

中韩的结果:

({'com.mansoon.BatteryDouble': 7048682,
  'com.google.android.googlequicksearchbox:interactor': 5614711,
  'com.google.android.googlequicksearchbox:search': 4320737,
  'com.google.android.gms.persistent': 4037772,
  'org.simalliance.openmobileapi.service:remote': 3127717,
  'com.facebook.orca': 2565712,
  'com.facebook.lite:fbns': 2165906,
  'com.whatsapp': 2130342,
  'com.facebook.katana': 2121389,
  'com.instagram.android:mqtt': 1884579},
 {'com.mansoon.BatteryDouble': 17313967,
  'com.google.android.googlequicksearchbox:search': 14933720,
  'com.google.android.googlequicksearchbox:interactor': 14228639,
  'org.simalliance.openmobileapi.service:remote': 11804923,
  'com.facebook.orca': 7960282,
  'com.google.android.gms.persistent': 6953279,
  'com.instagram.android:mqtt': 6149233,
  'com.android.systemui.recents': 5069114,
  'com.whatsapp': 4827753,
  'com.sec.spp.push:RemoteDlcProcess': 4811125})

大数据的处理方法:每一次只打开一个文件,只通过这个文件获取到想要的东西,然后再读取另一个文件的时候将获得到的相似的东西通过某些特定的函数整合到一起,保存在内存中,经过这样不断迭代,直到读取完所有的文件。这样的好处是内存占用很少,但是限于文件的数量、大小、cpu的速度、硬盘的读取速度,读取并操作完所有的文件的速度还是很慢的。

你可能感兴趣的:(笔记,python学习,学习记录,python)