Data_analysis(一)Kaggle上对StandarBank推荐产品的NAN数据进行fill

来源:互联网 发布:新浪微博数据冗余 编辑:程序博客网 时间:2024/04/28 15:56
 Kaggle上的一个比赛,https://www.kaggle.com/c/santander-product-recommendation 下面是一位大兄弟写的kernel,对数据进行清洗和nan填充  https://www.kaggle.com/apryor6/santander-product-recommendation/detailed-cleaning-visualization-python 在本次比赛中,您将获得来自Santander银行的1.5年客户行为数据,以预测客户将购买的新产品。

数据从2015-01-28开始,并有客户产品的每月记录,如“信用卡”,“储蓄帐户”等。

———————–这次主要是为了学习pandas库和数据处理入门—不喜轻拍————–

# -*- coding: utf-8 -*-"""Created on Mon Nov 07 10:05:36 2016@author: Sirius"""import numpy as npimport pandas as pdimport seaborn as snsimport matplotlib.pyplot as plt#data inputlimit_rows   = 7000000df= pd.read_csv('train_ver2.csv',                dtype={"sexo":str,                        "ind_nuevo":str,                        "ult_fec_cli_1t":str,                        "indext":str}, nrows=limit_rows)unique_ids= pd.Series(df["ncodpers"].unique())limit_people= 1e4unique_id= unique_ids.sample(n=limit_people)df= df[df.ncodpers.isin(unique_id)]df.describe() #获取每个数据的均值、方差、最大最小值等df.isnull().any() #判断是否有数据缺失"""fecha_dato --标识日期   fecha_alta --客户成为银行第一个合同的第一个持有人的日期   ---有缺失ncodpers  --客户代码ind_empleado --     Employee index: A active, B ex employed, F filial, N not employee, P pasivepais_residencia  --客户所在国sexo   --客户性别    ---有缺失age  --年龄ind_nuevo --新客户指数。1如果客户在过去6个月注册。  ---有缺失antiguedad  --客户工龄(几个月) indrel  -- 1(第一/初级),99(主要客户在本月,但不是在月底)   ---有缺失ult_fec_cli_1t ---作为主要客户(如果他不是在月底)的最后日期    ---有缺失indrel_1mes --客户类型在月初,1(第一/主要客户),2(共同拥有者),P(潜在),3(原初级),4(前共同所有人) ---有缺失tiprel_1mes --客户关系类型在月初,A(活动),I(不活动),P(前客户),R(潜在)   ---有缺失indresi ----如果居住国与银行所在国相同,居住指数(S(是)或N否)   ---有缺失indext ---外国人指数(S(是)或N 否)如果客户的出生国家不同于银行国家)   ---有缺失conyuemp --配偶指数。 1如果客户是雇员的配偶  ---有缺失canal_entrada  --客户加入的渠道    ---有缺失indfall ---已故指数。 N / S     ---有缺失tipodom --- 地址类型。 1,主地址   ---有缺失cod_prov --- 省代码(客户地址)   ---有缺失nomprov ---  省名称      ---有缺失ind_actividad_cliente     ---活动索引(1,活动客户; 0,不活动客户)   ---有缺失renta  ---家庭总收入    ---有缺失segmento  ----细分:01 - VIP,02 - 个人03 - 大学毕业生    ---有缺失-----------the product  will recommendative for custom-----------ind_ahor_fin_ult1---储蓄账户ind_aval_fin_ult1---担保ind_cco_fin_ult1----活期账户ind_cder_fin_ult1 derivada--帐户ind_cno_fin_ult1---工资帐户ind_ctju_fin_ult1---初级帐户ind_ctma_fin_ult1 ---Má的特别考虑ind_ctop_fin_ult1----特定帐户ind_ctpp_fin_ult1----特别账户ind_deco_fin_ult1----短期存款ind_deme_fin_ult1----中期存款ind_dela_fin_ult1----长期存款ind_ecue_fin_ult1----电子账户ind_fond_fin_ult1----基金ind_hip_fin_ult1-----抵押ind_plan_fin_ult1----养老金ind_pres_fin_ult1----贷款ind_reca_fin_ult1----税ind_tjcr_fin_ult1----信用卡ind_valo_fin_ult1----证券ind_viv_fin_ult1-----家庭账户ind_nomina_ult1------工资   ---有缺失ind_nom_pens_ult1----养老金   ---有缺失ind_recibo_ult1------直接借记""""""----把最后一个月从训练数据集中分离出来并构建一个新的特征,这个特征用来指示客户是否会购买新产品"""df["fecha_dato"] = pd.to_datetime(df["fecha_dato"],format="%Y-%m-%d")df["fecha_alta"] = pd.to_datetime(df["fecha_alta"],format="%Y-%m-%d")df["fecha_dato"].unique()"""认为客户会在某些特定的月份(圣诞节)更加喜欢购买产品,因此增加一个月份属性。"""df["month"] = pd.DatetimeIndex(df["fecha_dato"]).monthdf["age"]   = pd.to_numeric(df["age"], errors="coerce")"""            ------------------数据清洗----------------------------""""""   1.先对年龄进行分析:   除了缺失的值,有些人的年纪非常小,有些人的年纪很大,分布图呈双峰。大学生年龄段的占一大部分,另外一个峰是中年人。   把双峰分布分离,并把异常值移动到和它最近的一个平均值"""# loc --#获取选择区域内的数据,逗号前是行范围,逗号后是列范围,注loc通过标签选择数据,iloc通过位置选择数据df.loc[df.age < 18,"age"]  = df.loc[(df.age >= 18) & (df.age <= 30),"age"].mean(skipna=True)df.loc[df.age > 100,"age"] = df.loc[(df.age >= 30) & (df.age <= 100),"age"].mean(skipna=True)#df.fillna() 填充值df.fillna({1:0,2:0.5}) 对第一列nan值赋0,第二列赋值0.5df["age"].fillna(df["age"].mean(),inplace=True)df["age"]= df["age"].astype(int)"""   2.对ind_nuevo进行分析:该值是表示这个客户是新的还是旧。"""df["ind_nuevo"].isnull().sum()  #329/76749,缺失329#是否可以通过查看这些客户有多少个月的历史记录来填补缺失的值。months_active = df.loc[df["ind_nuevo"].isnull(),:].groupby("ncodpers", sort=False).size()#这里的意思是:取df中属性为ind_nuevo的所有的缺失值,这些值对应df中的所有属性,再提取这些所有属性中ncodpers的属性值months_active.max() #最大是6,说明这些缺失的客户都是最近6个月内活动的,说明是新的#这些id客户看起来都是新的,因此相应的填充缺失值df.loc[df["ind_nuevo"].isnull(),"ind_nuevo"] = 1"""   3.对antiguedad客户工龄(几个月)这个属性进行分析:"""df.antiguedad = pd.to_numeric(df.antiguedad,errors="coerce") #将参数转换为数字类型,如果有无效值,则置为NAN值np.sum(df["antiguedad"].isnull())  #缺失329个,和ind_nuevo客户是否为新一样df.loc[df["antiguedad"].isnull(),"ind_nuevo"].describe()#同样的人#因此,给他们最低的资历,因为是新加入的客户df.loc[df.antiguedad.isnull(),"antiguedad"] = df.antiguedad.min()df.loc[df.antiguedad <0]= 0"""   4.对indrel进行分析, --- 1(第一/初级),99(主要客户在本月,但不是在月底)   这听起来像一个有前途的功能。 我不知道如果主要状态是客户选择或公司分配的东西,但无论如何,似乎直观的是,正在下降的客户可能有不同的购买行为。"""pd.Series([i for i in df.indrel]).value_counts() #1有76301个,0有339个,99有109个df.loc[df.indrel.isnull(),"indrel"] = 1"""   5. 对tipodom进行分析: 地址类型。 1,主地址         对cod_prov分析 --- 省代码(客户地址)   ---有缺失    ***tippdom看起来用处不大,cod_prov也不需要,因为可以从nomprov中获得省份的名字    因此把它们剔除"""df.drop(["tipodom","cod_prov"],axis=1,inplace=True)df.isnull().any() #还差 ult_fec_cli_1t和indrel_1mes、tidrel_1mes、conyuemp  、canal_entrada、cod_prov、nomprov 、renta"""   6.对nomprov省名称进行分析:"""df.nomprov.unique()#unicode字符有一个问题。 我会手动修复它,#但如果有人知道一个更好的方法来捕获这样的情况,我会很高兴听到它在评论中。df.loc[df.nomprov=="CORU\xc3\x91A, A","nomprov"] = "CORUNA, A"df.loc[df.nomprov.isnull(),"nomprov"] = "UNKNOWN""""  7.对renta客户家庭总收入进行分析:  这里是一个缺少很多值的feature。 而不是只用一个中位数填充它们,它可能更准确地按区域分区。  为此,让我们来看看按地区的收入中位数,并在竞争的精神让我们的颜色像西班牙国旗。"""df.renta.isnull().sum() #-家庭总收入  缺失13567#通过观测每个省份的人均收入,知道有很多变化,所以我认为通过省份来填充renta的缺失是一个好主意。#首先按城市分组数据,然后减小以获得中值。 此中间数据帧由原始城市名称连接以扩展聚合的中位数收入,#按顺序排列,以便在行之间有1对1映射,最后替换缺失值。grouped        = df.groupby("nomprov").agg({"renta":lambda x: x.median(skipna=True)}).reset_index()new_incomes    = pd.merge(df,grouped,how="inner",on="nomprov").loc[:, ["nomprov","renta_y"]]new_incomes    = new_incomes.rename(columns={"renta_y":"renta"}).sort_values("renta").sort_values("nomprov")df.sort_values("nomprov",inplace=True)df             = df.reset_index()new_incomes    = new_incomes.reset_index()df.loc[df.renta.isnull(),"renta"] = new_incomes.loc[df.renta.isnull(),"renta"].reset_index()df.loc[df.renta.isnull(),"renta"] = df.loc[df.renta.notnull(),"renta"].median()"""  8. 对ind_nomina_ult1、ind_nom_pens_ult1进行分析:    它是一个布尔指示符,表示该产品是否在该月份拥有"""df.ind_nomina_ult1.isnull().sum() #缺失三个,量比较少,因此直接令缺少值为0df.ind_nom_pens_ult1.isnull().sum() #缺失三个,量比较少,因此直接令缺少值为0df.loc[df.ind_nomina_ult1.isnull(), "ind_nomina_ult1"] = 0df.loc[df.ind_nom_pens_ult1.isnull(), "ind_nom_pens_ult1"] = 0"""  9. 对空字符串进行处理  还有一堆包含空字符串的字符列。 在R中,这些被保留为空字符串而不是像在熊猫中的NA。  我最初在R中使用缺少值的数据,所以如果你想知道为什么我跳过一些NA列在这里,这就是为什么。  我会现在照顾他们。 在大多数情况下,具有NA的条目将转换为未知类别。首先,我将只获取具有缺失值的列。 然后打印唯一值以确定我应该填写什么。"""string_data = df.select_dtypes(include=["object"])missing_columns = [col for col in string_data if string_data[col].isnull().any()]for col in missing_columns:    print("Unique values for {0}:\n{1}\n".format(col,string_data[col].unique()))del string_data#好吧,基于这一点和每个变量的定义,我将填充空字符串与最常见的值或创建一个未知的类别,基于我认为更有意义。df.loc[df.indfall.isnull(),"indfall"] = "N"df.loc[df.tiprel_1mes.isnull(),"tiprel_1mes"] = "A"df.loc[df.tiprel_1mes.isnull(),"tiprel_1mes"] = "A"df.tiprel_1mes = df.tiprel_1mes.astype("category")unknown_cols = [col for col in missing_columns if col not in ["indfall","tiprel_1mes"]]for col in unknown_cols:    df.loc[df[col].isnull(),col] = "UNKNOWN"df.isnull().any() #至此,完成对所有缺失数据的填补"""       -----------------------------特征转换----------------------------------------------""""""现在为主要事件。 要了解增加或删除服务的客户趋势,我将为每个产品和月份创建一个标签,指明客户在该结算周期中是否添加,删除或维护了该服务。 我将通过为每个唯一的时间戳分配一个数字ID,然后将每个条目与上一个月匹配。 然后,每个产品的指标值的差异给出了期望值。"""unique_months = pd.DataFrame(pd.Series(df.fecha_dato.unique()).sort_values()).reset_index(drop=True)unique_months["month_id"] = pd.Series(range(1,1+unique_months.size)) # start with month 1, not 0 to match what we already haveunique_months["month_next_id"] = 1 + unique_months["month_id"]unique_months.rename(columns={0:"fecha_dato"},inplace=True)df = pd.merge(df,unique_months,on="fecha_dato")"""现在我将建立一个函数,将月份转换为一个有意义的标签。 每个月,客户可以保持其特定产品的当前状态,添加或放弃。"""def status_change(x):    diffs = x.diff().fillna(0)# first occurrence will be considered Maintained,     #which is a little lazy. A better way would be to check if     #the earliest date was the same as the earliest we have in the dataset    #and consider those separately. Entries with earliest dates later than that have     #joined and should be labeled as "Added"    label = ["Added" if i==1 \         else "Dropped" if i==-1 \         else "Maintained" for i in diffs]    return label"""现在我们可以实际应用这个函数到每个功能使用group by,然后通过transform广播结果回来"""#df.loc[:, feature_cols] = df.loc[:, [i for i in feature_cols]+["ncodpers"]].groupby("ncodpers").transform(status_change)"""我只想看看什么影响人们添加或删除服务,所以我会删除任何“维护”的实例。"""#df = pd.melt(df, id_vars   = [col for col in df.columns if col not in feature_cols],#            value_vars= [col for col in feature_cols])#df = df.loc[df.value!="Maintained",:]#df.shape
0 0