数据分析06-五个pandas可视化项目

数据分析-06

数据分析-06

pandas可视化

基本绘图
Series数据可视化

Series提供了plot方法以index作为x,以value作为y,完成数据可视化:

ts = pd.Series(np.random.randn(1000),
               index=pd.date_range('1/1/2000', periods=1000))
ts = ts.cumsum()
ts.plot()
DataFrame数据可视化

DataFrame提供了plot方法可以指定某一列作为x,某一列作为y,完成数据可视化:

df3 = pd.DataFrame(np.random.randn(1000, 2), 
                   columns=['B', 'C']).cumsum()
df3['A'] = np.arange(len(df3))
df3.plot(x='A', y='B')
高级绘图

plot()方法可以通过kind关键字参数提供不同的图像类型,包括:

类型说明
bar or barh柱状图
hist直方图
box箱线图
scatter散点图
pie饼状图

相关API如下:

# 柱状图
series.plot.bar()
dataFrame.plot.bar()
dataFrame.plot.barh()

直方图

# 直方图
series.plot.hist(alpha=0.5, bins=5)
dataFrame.plot.hist(alpha=0.5, bins=5)

散点图

# 散点图
df.plot.scatter(x='a', y='b', c=col, colormap='');

饼状图

# 饼状图
series.plot.pie(figsize=(6, 6))
dataFrame.plot.pie(subplots=True, figsize=(6, 6), layout=(2, 2))

箱线图

在这里插入图片描述

# 箱线图
# 先找出一组数据的上边缘、下边缘、中位数和两个四分位数;然后, 连接两个四分位数画出箱体;再将上边缘和下边缘与箱体相连接,中位数在箱体中间
df.plot.box()
# 分组箱线图
df.boxplot(by='X')

箱线图反应一组数据的集中趋势,四分位数的差可以反映一组数据的离散情况:

  1. 中位数高,表示平均水平较高;反之则表示平均水平较低。
  2. 箱子短,表示数据集中;箱子长,表示数据分散。

代码总结

pandas可视化
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
基本绘图
# Series可视化
s = pd.Series(np.random.normal(100, 10, 10), 
              index=pd.date_range('2020-01-01', periods=10))
s.plot()
<matplotlib.axes._subplots.AxesSubplot at 0x1bf39235be0>

在这里插入图片描述

ts = pd.Series(np.random.randn(1000),
               index=pd.date_range('1/1/2000', periods=1000))
ts = ts.cumsum()
ts.plot()
<matplotlib.axes._subplots.AxesSubplot at 0x1bf392a52b0>

在这里插入图片描述

# DataFrame可视化
data = np.random.normal(0, 1, (10, 2))  # 维度为(10,2)的一组随机数
df = pd.DataFrame(data, columns=['A', 'B'])
df['C'] = np.arange(10)
df
ABC
0-1.715010-1.1055320
1-0.059422-0.4448241
2-0.6217980.6537772
3-2.577156-0.4068373
4-2.208147-0.1889474
5-0.120376-1.2994485
6-0.6095140.6118296
7-0.5094990.6823367
80.873368-1.8087928
9-0.5983290.6188609
df.plot()
<matplotlib.axes._subplots.AxesSubplot at 0x1bf3932b080>

在这里插入图片描述

df.plot(x='C', y=['A', 'B'])
<matplotlib.axes._subplots.AxesSubplot at 0x1bf393ca2b0>

在这里插入图片描述

pandas高级绘图
# Series可视化
s = pd.Series(np.random.normal(100, 10, 10), 
              index=pd.date_range('2020-01-01', periods=10))
s.plot.barh(color='dodgerblue')
<matplotlib.axes._subplots.AxesSubplot at 0x1bf394417b8>

在这里插入图片描述

data = np.random.normal(80, 3, (10, 2))  # 维度为(10,2)的一组随机数
df = pd.DataFrame(data, columns=['A', 'B'])
df.plot.bar()
<matplotlib.axes._subplots.AxesSubplot at 0x1bf396ddc50>

在这里插入图片描述

pandas直方图
s.plot.hist(bins=20)
<matplotlib.axes._subplots.AxesSubplot at 0x1bf397e5f98>

在这里插入图片描述

pandas散点图
df.plot.scatter(x='A', y='B', s=80, c='A', cmap='jet')
<matplotlib.axes._subplots.AxesSubplot at 0x1bf39ab08d0>

在这里插入图片描述

pandas饼状图
values = [15, 13.3, 8.5, 7.3, 4.62, 51.28]
labels = ['Java', 'C', 'Python', 'C++', 'VB', 'Other']
s = pd.Series(values, index=labels)
s
Java      15.00
C         13.30
Python     8.50
C++        7.30
VB         4.62
Other     51.28
dtype: float64
s.plot.pie(figsize=(6,6), startangle=90, shadow=True)
<matplotlib.axes._subplots.AxesSubplot at 0x1bf3aefa898>

在这里插入图片描述

df = pd.DataFrame(s, columns=['A'])
df['B'] = [14.1, 3, 18.2, 8, 2, 30.2]
df.plot.pie(subplots=True, figsize=(8,4), layout=(1,2))
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x000001BF3CAEA390>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001BF3CB6DF28>]],
      dtype=object)

在这里插入图片描述

箱线图
data = pd.read_csv('../data/学生考试表现数据/StudentsPerformance.csv')
ms = data['math score']
ms.plot.box()
<matplotlib.axes._subplots.AxesSubplot at 0x1bf3cc080b8>

在这里插入图片描述

df = data[['math score', 'writing score', 'reading score']]
df.plot.box()
<matplotlib.axes._subplots.AxesSubplot at 0x1bf3cc6f5c0>

在这里插入图片描述



项目资源下载:

在我的资源文件中下载
下载地址:https://download.csdn.net/download/yegeli/12562286

项目一:分析影响学生成绩的因素

学生成绩影响因素分析
import numpy as np
import pandas as pd
data = pd.read_csv('StudentsPerformance.csv')
data['total score'] = data.sum(axis=1)
# 参数number,object意为:统计数字列与字符串列
data.describe(include=['number', 'object'])
genderrace/ethnicityparental level of educationlunchtest preparation coursemath scorereading scorewriting scoretotal score
count100010001000100010001000.000001000.0000001000.0000001000.000000
unique25622NaNNaNNaNNaN
topfemalegroup Csome collegestandardnoneNaNNaNNaNNaN
freq518319226645642NaNNaNNaNNaN
meanNaNNaNNaNNaNNaN66.0890069.16900068.054000203.312000
stdNaNNaNNaNNaNNaN15.1630814.60019215.19565742.771978
minNaNNaNNaNNaNNaN0.0000017.00000010.00000027.000000
25%NaNNaNNaNNaNNaN57.0000059.00000057.750000175.000000
50%NaNNaNNaNNaNNaN66.0000070.00000069.000000205.000000
75%NaNNaNNaNNaNNaN77.0000079.00000079.000000233.000000
maxNaNNaNNaNNaNNaN100.00000100.000000100.000000300.000000
# 分析性别对学习成绩的影响(按性别分组)
r = data.pivot_table(index='gender')
r
math scorereading scoretotal scorewriting score
gender
female63.63320572.608108208.70849472.467181
male68.72821665.473029197.51244863.311203
# 可视化
r.T.plot.barh()
<matplotlib.axes._subplots.AxesSubplot at 0x24839422d00>

在这里插入图片描述

r.T.plot.pie(subplots=True,figsize=(12,3))
array([<matplotlib.axes._subplots.AxesSubplot object at 0x000002483B50FFA0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x000002483B56EE80>],
      dtype=object)

在这里插入图片描述

总体来说,女生的成绩普遍比较好,但是男生更善于数学。

# 分析种族对学习成绩的影响
r = data.pivot_table(index='race/ethnicity')
r
math scorereading scoretotal scorewriting score
race/ethnicity
group A61.62921364.674157188.97752862.674157
group B63.45263267.352632196.40526365.600000
group C64.46395069.103448201.39498467.827586
group D67.36259570.030534207.53816870.145038
group E73.82142973.028571218.25714371.407143

种族划分(优秀-及格): E - D - C - B - A

r.T.plot.barh()
<matplotlib.axes._subplots.AxesSubplot at 0x2483b5f6370>

在这里插入图片描述

# 分析父母教育水平对学习成绩的影响
r = data.pivot_table(index='parental level of education')
r.sort_values(by='total score')
math scorereading scoretotal scorewriting score
parental level of education
high school62.13775564.704082189.29081662.448980
some high school63.49720766.938547195.32402264.888268
some college67.12831969.460177205.42920468.840708
associate's degree67.88288370.927928208.70720769.896396
bachelor's degree69.38983173.000000215.77118673.381356
master's degree69.74576375.372881220.79661075.677966
# 可视化
r.T.plot.barh()
<matplotlib.axes._subplots.AxesSubplot at 0x2483b672c70>

在这里插入图片描述

父母受教育水平越高,学习成绩越好。

# 分析中午饭学习成绩的影响
r = data.pivot_table(index='lunch')
r.sort_values(by='total score', ascending=False)
math scorereading scoretotal scorewriting score
lunch
standard70.03410971.654264212.51162870.823256
free/reduced58.92112764.653521186.59718363.022535
# 可视化
r.plot.pie(subplots=True,figsize=(12,3))
array([<matplotlib.axes._subplots.AxesSubplot object at 0x000002483B738310>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x000002483B75CFA0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x000002483B7891C0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x000002483B7B71F0>],
      dtype=object)

在这里插入图片描述

# 分析测试对成绩的影响
r = data.pivot_table(index='test preparation course')
r.sort_values(by='total score', ascending=False)
math scorereading scoretotal scorewriting score
test preparation course
completed69.69553173.893855218.00838074.418994
none64.07788266.534268195.11682264.504673
# 可视化
r.plot.pie(subplots=True,figsize=(12,3))
array([<matplotlib.axes._subplots.AxesSubplot object at 0x000002483B57B280>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x000002483B768DC0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x000002483B67F9D0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x000002483B8A43D0>],
      dtype=object)

在这里插入图片描述

r = data.pivot_table(index=['gender','test preparation course'])
r
math scorereading scoretotal scorewriting score
gendertest preparation course
femalecompleted67.19565277.375000223.36413078.793478
none61.67065969.982036200.63473168.982036
malecompleted72.33908070.212644212.34482869.793103
none66.68831262.795455189.13311759.649351
分析前100名与后100名同学的不同情况
r = data.sort_values(by='total score', ascending=False)
top100 = r.head(100)
tail100 = r.tail(100)
r1 = pd.DataFrame({'top100':top100['gender'].value_counts(),
              'tail100':tail100['gender'].value_counts()})
r1
top100tail100
female6638
male3462
r1.plot.pie(subplots=True,figsize=(8,4))
array([<matplotlib.axes._subplots.AxesSubplot object at 0x000002483B93EA30>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x000002483B963BE0>],
      dtype=object)

在这里插入图片描述

data = data['parental level of education'].value_counts()
data.plot.pie(figsize=(6,6))
<matplotlib.axes._subplots.AxesSubplot at 0x2483c98cf70>

在这里插入图片描述

data.plot.barh()
<matplotlib.axes._subplots.AxesSubplot at 0x2483c9daf70>

在这里插入图片描述

r2 = pd.DataFrame({'top100':top100['parental level of education'].value_counts(),
              'tail100':tail100['parental level of education'].value_counts()})
r2
top100tail100
associate's degree2917
bachelor's degree208
high school632
master's degree151
some college2114
some high school928
r2.plot.barh()
<matplotlib.axes._subplots.AxesSubplot at 0x2483ca2e550>

在这里插入图片描述

  • 总结:

      总体来说,女生的成绩普遍比较好,但是男生更善于数学。
      对于种族特征来讲, 优秀~良好:E - D - C - B - A 
      父母受教育水平越高,学习成绩越好。
      建议从以下几个方面提高学生的学习成绩:
      建议每位同学吃好中午饭。
      建议每位同学尽量完成预科班考试。
    

项目二:泰坦尼克号生存人员数据分析与可视化

Kaggle案例泰坦尼克号生存预测分析
查看数据

用pandas加载数据

import pandas as pd #数据分析
import numpy as np #科学计算
data_train=pd.read_csv('train.csv')
data_train.head()
data_train.columns
Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
       'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'],
      dtype='object')
data_train.head()
PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
0103Braund, Mr. Owen Harrismale22.010A/5 211717.2500NaNS
1211Cumings, Mrs. John Bradley (Florence Briggs Th...female38.010PC 1759971.2833C85C
2313Heikkinen, Miss. Lainafemale26.000STON/O2. 31012827.9250NaNS
3411Futrelle, Mrs. Jacques Heath (Lily May Peel)female35.01011380353.1000C123S
4503Allen, Mr. William Henrymale35.0003734508.0500NaNS

有以下这些字段

PassengerId => 乘客ID

Survived => 生存

Pclass => 乘客等级(1/2/3等舱位)

Name => 乘客姓名

Sex => 性别

Age => 年龄

SibSp => 堂兄弟/妹个数

Parch => 父母与小孩个数

Ticket => 船票信息

Fare => 票价

Cabin => 客舱

Embarked => 登船港口

数据简单描述性分析
data_train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
训练数据中总共有891名乘客,但是我们有些属性的数据不全,比如说:
  • Age(年龄)属性只有714名乘客有记录
  • Cabin(客舱)更是只有204名乘客是已知的

    具体数据数值情况,我们用下列的方法,得到数值型数据的一些分布
data_train.describe()
PassengerIdSurvivedPclassAgeSibSpParchFare
count891.000000891.000000891.000000714.000000891.000000891.000000891.000000
mean446.0000000.3838382.30864229.6991180.5230080.38159432.204208
std257.3538420.4865920.83607114.5264971.1027430.80605749.693429
min1.0000000.0000001.0000000.4200000.0000000.0000000.000000
25%223.5000000.0000002.00000020.1250000.0000000.0000007.910400
50%446.0000000.0000003.00000028.0000000.0000000.00000014.454200
75%668.5000001.0000003.00000038.0000001.0000000.00000031.000000
max891.0000001.0000003.00000080.0000008.0000006.000000512.329200

mean字段告诉我们,大概0.383838的人最后获救了,平均乘客年龄大概是29.7岁

通过可视化的方式深入了解数据
获救情况人数可视化
import matplotlib.pyplot as plt

# 显示中文
from pylab import mpl
mpl.rcParams['font.sans-serif']=['Simhei']  #显示中文
mpl.rcParams['axes.unicode_minus']= False

data_train.Survived.value_counts().plot(kind='bar')
plt.title('获救情况(1为获救)')
plt.ylabel('人数')
plt.legend()
plt.show()

在这里插入图片描述

乘客等级分布可视化
data_train.Pclass.value_counts().plot(kind='bar')
plt.ylabel('人数')
plt.xlabel('乘客等级')
plt.title('乘客等级分布情况')
print(data_train.Pclass.value_counts())
3    491
1    216
2    184
Name: Pclass, dtype: int64

在这里插入图片描述

按年龄看获救分布可视化
data_train['Age'].plot.kde()
<matplotlib.axes._subplots.AxesSubplot at 0x1da647b4358>

在这里插入图片描述

plt.scatter(data_train.Survived,data_train.Age)
plt.ylabel('年龄')
plt.grid(axis='y')
plt.title('按照年龄看获救分布可视化(1为获救)')
plt.show()

在这里插入图片描述

各等级的乘客年龄分布
# 各等级的乘客年龄分布密度图
data_train.Age[data_train.Pclass == 1].plot(kind='kde')
data_train.Age[data_train.Pclass == 2].plot(kind='kde')
data_train.Age[data_train.Pclass == 3].plot(kind='kde')
plt.xlabel('年龄')
plt.ylabel('密度')
plt.title('各等级的乘客年龄分布')
plt.legend(('一等舱','二等舱','三等舱'))
plt.show()

在这里插入图片描述

各登船口岸上船人数可视化
data_train.Embarked.value_counts().plot(kind='bar')
plt.title('各登船港口上船人数')
plt.ylabel('人数')
plt.show()

在这里插入图片描述

所以我们在图上可以看出来:
  • 被救的人300多点,不到半数;
  • 3等舱乘客非常多;遇难和获救的人年龄跨度都很广;
  • 3个不同的舱年龄总体趋势似乎也一致,2/3等舱乘客20岁多点的人最多,1等舱40岁左右的最多
  • 登船港口人数按照S、C、Q递减,而且S远多于另外俩港口。>

查看每一个属性与获救情况的可视化
各乘客等级的获救情况
#看看各乘客等级的获救情况
Survived_1=data_train.Pclass[data_train.Survived==1].value_counts()
# Survived_1
Survived_0=data_train.Pclass[data_train.Survived==0].value_counts()
df=pd.DataFrame({'获救':Survived_1,'未获救':Survived_0})
df.plot(kind='bar')

plt.title('各乘客等级的获救情况可视化')
plt.xlabel('乘客等级')
plt.ylabel('人数')
# plt.legend()

plt.show()

在这里插入图片描述

各登船港口对于获救情况分析
#看看各登船港口的获救情况
Survived_1=data_train.Embarked[data_train.Survived==1].value_counts()
# Survived_1
Survived_0=data_train.Embarked[data_train.Survived==0].value_counts()
df=pd.DataFrame({'获救':Survived_1,'未获救':Survived_0})
df.plot(kind='bar')

plt.title('各登船港口的获救情况可视化')
plt.xlabel('登船港口')
plt.ylabel('人数')
# plt.legend()

plt.show()

在这里插入图片描述

各性别的获救情况
#看看各性别的获救情况
Survived_m=data_train.Survived[data_train.Sex=='male'].value_counts()
# Survived_m
Survived_f=data_train.Survived[data_train.Sex=='female'].value_counts()
df=pd.DataFrame({'男性':Survived_m,'女性':Survived_f})
df.plot(kind='bar')

plt.title('按照性别看获救情况')
plt.xlabel('获救')
plt.ylabel('人数')
plt.show()

在这里插入图片描述

获救的女性要多于男性。

堂兄弟和父母字段对于获救情况分析
# 堂兄弟/妹个数
data_train.pivot_table(index=['SibSp', 'Survived'], values='PassengerId', aggfunc='count')
PassengerId
SibSpSurvived
00398
1210
1097
1112
2015
113
3012
14
4015
13
505
807
# 父母个数
data_train.pivot_table(index=['Parch', 'Survived'], values='PassengerId', aggfunc='count')
PassengerId
ParchSurvived
00445
1233
1053
165
2040
140
302
13
404
504
11
601

ticket是船票编号,是unique的,和最后的结果没有太大的关系,不纳入考虑的特征范畴

cabin只有204个乘客有值,我们先看看它的一个分布

#cabin只有204个乘客有值,我们先看看它的一个分布
data_train.Cabin.value_counts()
G6             4
C23 C25 C27    4
B96 B98        4
F2             3
E101           3
              ..
D30            1
A7             1
D47            1
E31            1
C99            1
Name: Cabin, Length: 147, dtype: int64
# 分析cabin这个值的有无,对于survival的分布状况
survival_cabin=data_train.Survived[pd.notnull(data_train.Cabin)].value_counts()
survival_cabin
survival_nocabin=data_train.Survived[pd.isnull(data_train.Cabin)].value_counts()
df=pd.DataFrame({'有':survival_cabin,'无':survival_nocabin})
df.plot(kind='bar')
plt.title('按照Cabin有无去看获救情况')
plt.xlabel('获救情况')
plt.ylabel('Cabin有无')
plt.show()

在这里插入图片描述

有Cabin记录的似乎获救概率稍高一些

数据预处理
# 打印数据前几行
data_train.head()
PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
0103Braund, Mr. Owen Harrismale22.010A/5 211717.2500NaNS
1211Cumings, Mrs. John Bradley (Florence Briggs Th...female38.010PC 1759971.2833C85C
2313Heikkinen, Miss. Lainafemale26.000STON/O2. 31012827.9250NaNS
3411Futrelle, Mrs. Jacques Heath (Lily May Peel)female35.01011380353.1000C123S
4503Allen, Mr. William Henrymale35.0003734508.0500NaNS
数据缺失值处理
# 查看数据
data_train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
#补充Age的缺失值
data_train['Age']=data_train['Age'].fillna(data_train['Age'].mean())

#按Cabin有无数据,将这个属性处理成Yes和No两种类型
def set_cabin(df):
    df.loc[(df.Cabin.notnull()),'Cabin']='Yes'
    df.loc[(df.Cabin.isnull()),'Cabin']='No'
    return df
data_train=set_cabin(data_train)
#对Embarked进行填充数据
data_train['Embarked']=data_train['Embarked'].fillna('S')
# 查看数据
data_train.head()
PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
0103Braund, Mr. Owen Harrismale22.010A/5 211717.2500NoS
1211Cumings, Mrs. John Bradley (Florence Briggs Th...female38.010PC 1759971.2833YesC
2313Heikkinen, Miss. Lainafemale26.000STON/O2. 31012827.9250NoS
3411Futrelle, Mrs. Jacques Heath (Lily May Peel)female35.01011380353.1000YesS
4503Allen, Mr. William Henrymale35.0003734508.0500NoS
data_train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            891 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          891 non-null object
Embarked       891 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
数据one-hot处理

因为逻辑回归建模时,需要输入的特征都是数值型特征,我们通常会先对类目型的特征因子化/one-hot编码

什么叫做因子化/one-hot编码?举个例子

以Embarked为例,原本一个属性维度,因为其取值可以是[‘S’,’C’,’Q‘],而将其平展开为’Embarked_C’,’Embarked_S’, ‘Embarked_Q’三个属性

  • 原本Embarked取值为S的,在此处的”Embarked_S”下取值为1,在’Embarked_C’, ‘Embarked_Q’下取值为0
  • 原本Embarked取值为C的,在此处的”Embarked_C”下取值为1,在’Embarked_S’, ‘Embarked_Q’下取值为0
  • 原本Embarked取值为Q的,在此处的”Embarked_Q”下取值为1,在’Embarked_C’, ‘Embarked_S’下取值为0
  • 我们使用pandas的”get_dummies”来完成这个工作,并拼接在原来的”data_train”之上
# 因为逻辑回归建模时,需要输入的特征都是数值型特征
# 我们先对类目型的特征离散/因子化
# 以Cabin为例,原本一个属性维度,因为其取值可以是['yes','no'],而将其平展开为'Cabin_yes','Cabin_no'两个属性
# 原本Cabin取值为yes的,在此处的'Cabin_yes'下取值为1,在'Cabin_no'下取值为0
# 原本Cabin取值为no的,在此处的'Cabin_yes'下取值为0,在'Cabin_no'下取值为1
# 我们使用pandas的get_dummies来完成这个工作,并拼接在原来的data_train之上,如下所示
# Cabin,Embarked,Pclass,Sex
dummies_Cabin=pd.get_dummies(data_train['Cabin'],prefix='Cabin')
dummies_Embarked=pd.get_dummies(data_train['Embarked'],prefix='Cabin')
dummies_Pclass=pd.get_dummies(data_train['Pclass'],prefix='Pclass')
dummies_Sex=pd.get_dummies(data_train['Sex'],prefix='Sex')

df=pd.concat([data_train,dummies_Cabin,dummies_Embarked,dummies_Pclass,dummies_Sex],axis=1)
df.drop(['Pclass','Name','Sex','Ticket','Cabin','Embarked'],axis=1,inplace=True)
df.head()
PassengerIdSurvivedAgeSibSpParchFareCabin_NoCabin_YesCabin_CCabin_QCabin_SPclass_1Pclass_2Pclass_3Sex_femaleSex_male
01022.0107.25001000100101
12138.01071.28330110010010
23126.0007.92501000100110
34135.01053.10000100110010
45035.0008.05001000100101
df.describe()
PassengerIdSurvivedAgeSibSpParchFareCabin_NoCabin_YesCabin_CCabin_QCabin_SPclass_1Pclass_2Pclass_3Sex_femaleSex_male
count891.000000891.000000891.000000891.000000891.000000891.000000891.000000891.000000891.000000891.000000891.000000891.000000891.000000891.000000891.000000891.000000
mean446.0000000.38383829.6991180.5230080.38159432.2042080.7710440.2289560.1885520.0864200.7250280.2424240.2065100.5510660.3524130.647587
std257.3538420.48659213.0020151.1027430.80605749.6934290.4203970.4203970.3913720.2811410.4467510.4287900.4050280.4976650.4779900.477990
min1.0000000.0000000.4200000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.000000
25%223.5000000.00000022.0000000.0000000.0000007.9104001.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.000000
50%446.0000000.00000029.6991180.0000000.00000014.4542001.0000000.0000000.0000000.0000001.0000000.0000000.0000001.0000000.0000001.000000
75%668.5000001.00000035.0000001.0000000.00000031.0000001.0000000.0000000.0000000.0000001.0000000.0000000.0000001.0000001.0000001.000000
max891.0000001.00000080.0000008.0000006.000000512.3292001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
数据标准化处理

我们还得做一些处理,Age和Fare两个属性,乘客的数值幅度变化太大,进行标准差标准化处理

a=df.Age
df['Age_scaled']=(a-a.mean())/(a.std())
df=df.drop('Age',axis=1)

b=df.Fare
df['Fare_scaled']=(b-b.mean())/(b.std())
df=df.drop('Fare',axis=1)
df.head()
PassengerIdSurvivedSibSpParchCabin_NoCabin_YesCabin_CCabin_QCabin_SPclass_1Pclass_2Pclass_3Sex_femaleSex_maleAge_scaledFare_scaled
010101000100101-0.592148-0.502163
1211001100100100.6384300.786404
231001000100110-0.284503-0.488580
3411001001100100.4076970.420494
4500010001001010.407697-0.486064
数据建模–逻辑回归

我们把需要的feature字段取出来,转成numpy格式,使用scikit-learn中的LogisticRegression建模。

# 我们把需要的feature字段取出来,转成numpy格式,使用scikit-learn中的LogisticRegression建模
from sklearn import linear_model
train_df=df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
train_np=train_df.values
# y即Survival结果
y=train_np[:,0]
# X即特征属性值
X=train_np[:,1:]
# fit到RandomForestRegressor之中
clf=linear_model.LogisticRegression(penalty='l2')
clf.fit(X,y)
# 模型正确率
print(clf.score(X,y))
clf
0.8125701459034792





LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
          penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False)

penalty:惩罚项,str类型,可选参数为l1和l2,默认为l2。用于指定惩罚项中使用的规范。newton-cg、sag和lbfgs求解算法只支持L2规范。L1G规范假设的是模型的参数满足拉普拉斯分布,L2假设的模型参数满足高斯分布,所谓的范式就是加上对参数的约束,使得模型更不会过拟合(overfit)

tol:停止求解的标准,float类型,默认为1e-4。就是求解到多少的时候,停止,认为已经求出最优解。

c:正则化系数λ的倒数,float类型,默认为1.0。必须是正浮点型数。像SVM一样,越小的数值表示越强的正则化。

接下来咱们对训练集和测试集做一样的操作

# 读取测试集数据
data_test=pd.read_csv('test.csv')
data_test.head()
PassengerIdPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
08923Kelly, Mr. Jamesmale34.5003309117.8292NaNQ
18933Wilkes, Mrs. James (Ellen Needs)female47.0103632727.0000NaNS
28942Myles, Mr. Thomas Francismale62.0002402769.6875NaNQ
38953Wirz, Mr. Albertmale27.0003151548.6625NaNS
48963Hirvonen, Mrs. Alexander (Helga E Lindqvist)female22.011310129812.2875NaNS
# 描述分析数据
data_test.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
PassengerId    418 non-null int64
Pclass         418 non-null int64
Name           418 non-null object
Sex            418 non-null object
Age            332 non-null float64
SibSp          418 non-null int64
Parch          418 non-null int64
Ticket         418 non-null object
Fare           417 non-null float64
Cabin          91 non-null object
Embarked       418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 36.0+ KB
# 接着我们对test_data做和train_data中一致的特征变换
# 对Fare处理
data_test.loc[(data_test.Fare.isnull()),'Fare']=0
#补充Age的缺失值
data_test['Age']=data_test['Age'].fillna(data_test['Age'].mean())
#按Cabin有无数据,将这个属性处理成Yes和No两种类型
def set_Cabin(df):
    df.loc[(df.Cabin.notnull()),'Cabin']='Yes'
    df.loc[(df.Cabin.isnull()),'Cabin']='No'
    return df
data_test=set_cabin(data_test)

# one-hot编码
# Cabin,Embarked,Sex,Pclass
dummies_Cabin=pd.get_dummies(data_test['Cabin'],prefix='Cabin')
dummies_Embarked=pd.get_dummies(data_test['Embarked'],prefix='Embarked')
dummies_Pclass=pd.get_dummies(data_test['Pclass'],prefix='Pclass')
dummies_Sex=pd.get_dummies(data_test['Sex'],prefix='Sex')

df_test=pd.concat([data_test,dummies_Cabin,dummies_Embarked,dummies_Pclass,dummies_Sex],axis=1)
df_test.drop(['Pclass','Name','Sex','Ticket','Cabin','Embarked'],axis=1,inplace=True)

# 标准化处理数据  Age Fare
a=df_test.Age
df_test['Age_scaled']=(a-a.mean())/(a.std())
df_test=df_test.drop('Age',axis=1)

b=df_test.Fare
df_test['Fare_scaled']=(b-b.mean())/(b.std())
df_test=df_test.drop('Fare',axis=1)
df_test.head()
PassengerIdSibSpParchCabin_NoCabin_YesEmbarked_CEmbarked_QEmbarked_SPclass_1Pclass_2Pclass_3Sex_femaleSex_maleAge_scaledFare_scaled
08920010010001010.334592-0.496043
18931010001001101.323944-0.510885
28940010010010012.511166-0.462780
3895001000100101-0.259019-0.481127
4896111000100110-0.654760-0.416242
test=df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
predictions=clf.predict(test)
result=pd.DataFrame({'PassengerId':data_test['PassengerId'].values,
                     'Survived':predictions.astype(np.int32)})
result.to_csv('logistic_regression_predictions.csv',index=False)

# 读取logistic_regression_predictions.csv数据
pd.read_csv('logistic_regression_predictions.csv').head(10)
PassengerIdSurvived
08920
18930
28940
38950
48961
58970
68981
78990
89001
99010

项目三:movielens电影数据分析与可视化

movielens电影评分数据分析(上)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
读取数据
# 从用户表读取用户信息
users = pd.read_table('users.dat', header=None, names=['UserID','Gender','Age','Occupation','Zip-code'], sep='::',engine='python')
# 打印列表长度,共有6040条记录
print(len(users))
6040
# 查看前五条记录
users.head(5)
UserIDGenderAgeOccupationZip-code
01F11048067
12M561670072
23M251555117
34M45702460
45M252055455
# 同样方法,导入电影评分表
ratings = pd.read_table('ratings.dat', header=None, names=['UserID', 'MovieID', 'Rating', 'Timestamp'], sep='::',engine='python')
# 打印列表长度
print(len(ratings))
print(ratings.head(5))
# 同样方法,导入电影数据表
movies = pd.read_table('movies.dat', header=None, names=['MovieID', 'Title', 'Genres'], sep='::',engine='python')
print(len(movies))
print(movies.head(5))
1000209
   UserID  MovieID  Rating  Timestamp
0       1     1193       5  978300760
1       1      661       3  978302109
2       1      914       3  978301968
3       1     3408       4  978300275
4       1     2355       5  978824291
3883
   MovieID                               Title                        Genres
0        1                    Toy Story (1995)   Animation|Children's|Comedy
1        2                      Jumanji (1995)  Adventure|Children's|Fantasy
2        3             Grumpier Old Men (1995)                Comedy|Romance
3        4            Waiting to Exhale (1995)                  Comedy|Drama
4        5  Father of the Bride Part II (1995)                        Comedy
合并数据表
# 导入完成之后,我们可以发现这三张表类似于数据库中的表
# 要进行数据分析,我们就要将多张表进行合并才有助于分析 先将users与ratings两张表合并再跟movied合并
data = pd.merge(pd.merge(users, ratings), movies)
data.tail(5)
UserIDGenderAgeOccupationZip-codeMovieIDRatingTimestampTitleGenres
10002045949M18174790121985958846401Modulations (1998)Documentary
10002055675M35143003027033976029116Broken Vessels (1998)Drama
10002065780M18179288628451958153068White Boys (1999)Drama
10002075851F18205541036075957756608One Little Indian (1973)Comedy|Drama|Western
10002085938M2513540129094957273353Five Wives, Three Secretaries and Me (1998)Documentary
对数据初步描述分析
data.describe()
UserIDAgeOccupationMovieIDRatingTimestamp
count1.000209e+061.000209e+061.000209e+061.000209e+061.000209e+061.000209e+06
mean3.024512e+032.973831e+018.036138e+001.865540e+033.581564e+009.722437e+08
std1.728413e+031.175198e+016.531336e+001.096041e+031.117102e+001.215256e+07
min1.000000e+001.000000e+000.000000e+001.000000e+001.000000e+009.567039e+08
25%1.506000e+032.500000e+012.000000e+001.030000e+033.000000e+009.653026e+08
50%3.070000e+032.500000e+017.000000e+001.835000e+034.000000e+009.730180e+08
75%4.476000e+033.500000e+011.400000e+012.770000e+034.000000e+009.752209e+08
max6.040000e+035.600000e+012.000000e+013.952000e+035.000000e+001.046455e+09
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1000209 entries, 0 to 1000208
Data columns (total 10 columns):
 #   Column      Non-Null Count    Dtype 
---  ------      --------------    ----- 
 0   UserID      1000209 non-null  int64 
 1   Gender      1000209 non-null  object
 2   Age         1000209 non-null  int64 
 3   Occupation  1000209 non-null  int64 
 4   Zip-code    1000209 non-null  object
 5   MovieID     1000209 non-null  int64 
 6   Rating      1000209 non-null  int64 
 7   Timestamp   1000209 non-null  int64 
 8   Title       1000209 non-null  object
 9   Genres      1000209 non-null  object
dtypes: int64(6), object(4)
memory usage: 83.9+ MB
查看数据
# 合并后的每一条记录反映了每个人的年龄,职业,性别,邮编,电影ID,评分,时间戳,电影信息,电影分类等一系列信息
# 比如我们查看用户id为1的所有信息
data[data.UserID==1].head()
UserIDGenderAgeOccupationZip-codeMovieIDRatingTimestampTitleGenres
01F1104806711935978300760One Flew Over the Cuckoo's Nest (1975)Drama
17251F110480676613978302109James and the Giant Peach (1996)Animation|Children's|Musical
22501F110480679143978301968My Fair Lady (1964)Musical|Romance
28861F1104806734084978300275Erin Brockovich (2000)Drama
42011F1104806723555978824291Bug's Life, A (1998)Animation|Children's|Comedy
r = data['Zip-code'].value_counts()
r = r.sort_values(ascending=False).head(10)
r.plot(kind='bar')
plt.xticks(rotation=45)
plt.show()

在这里插入图片描述

# 查看评分次数多的电影并进行排序   data_rating_num接收
data_rating_num=data.groupby('Title').size()
data_rating_num.head(10)
Title
$1,000,000 Duck (1971)                37
'Night Mother (1986)                  70
'Til There Was You (1997)             52
'burbs, The (1989)                   303
...And Justice for All (1979)        199
1-900 (1994)                           2
10 Things I Hate About You (1999)    700
101 Dalmatians (1961)                565
101 Dalmatians (1996)                364
12 Angry Men (1957)                  616
dtype: int64
#进行排序
data_rating_num_sorted=data_rating_num.sort_values(ascending=False)
data_rating_num_sorted = data_rating_num_sorted[(data_rating_num_sorted>300) & (data_rating_num_sorted<400)]
data_rating_num_sorted
Title
Yellow Submarine (1968)          399
Anaconda (1997)                  399
Snow Falling on Cedars (1999)    398
His Girl Friday (1940)           397
First Blood (1982)               397
                                ... 
Godzilla (Gojira) (1954)         301
Rambo III (1988)                 301
Zero Effect (1998)               301
Short Cuts (1993)                301
Old Yeller (1957)                301
Length: 256, dtype: int64
查看每一部电影不同性别的平均评分并计算分歧差值,之后排序
# 查看每一部电影不同性别的平均评分 data_gender接收
data_gender=data.pivot_table(index='Title',columns='Gender',values='Rating',aggfunc='mean')
data_gender = data_gender.loc[data_rating_num_sorted.index]
data_gender.head()
GenderFM
Title
Yellow Submarine (1968)3.7142863.689286
Anaconda (1997)2.0000002.248447
Snow Falling on Cedars (1999)3.4820143.374517
His Girl Friday (1940)4.3125004.213439
First Blood (1982)3.2857143.599448
# 查看电影分歧最大的那部电影,在原数据中体现
data_gender['diff']=np.fabs(data_gender.F-data_gender.M)
data_gender.head()
GenderFMdiff
Title
Yellow Submarine (1968)3.7142863.6892860.025000
Anaconda (1997)2.0000002.2484470.248447
Snow Falling on Cedars (1999)3.4820143.3745170.107497
His Girl Friday (1940)4.3125004.2134390.099061
First Blood (1982)3.2857143.5994480.313733
# 男女电影分歧最大进行排序 data_gender_sorted接收
data_gender_sorted=data_gender.sort_values(by='diff',ascending=False)
data_gender_sorted_top10 = data_gender_sorted.head(10)
data_gender_sorted_top10
GenderFMdiff
Title
Kentucky Fried Movie, The (1977)2.8787883.5551470.676359
Jumpin' Jack Flash (1986)3.2547172.5783580.676359
Longest Day, The (1962)3.4117654.0314470.619682
Cable Guy, The (1996)2.2500002.8637870.613787
For a Few Dollars More (1965)3.4090913.9537950.544704
Porky's (1981)2.2968752.8363640.539489
Fright Night (1985)2.9736843.5000000.526316
Anastasia (1997)3.8000003.2816090.518391
French Kiss (1995)3.5357143.0569620.478752
Little Shop of Horrors, The (1960)3.6500003.1796880.470312
genres = movies.set_index(movies['Title']).loc[data_gender_sorted_top10.index].Genres
data_gender_sorted_top10['Genres'] = genres
data_gender_sorted_top10
<ipython-input-16-f0465e0ad586>:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  data_gender_sorted_top10['Genres'] = genres
GenderFMdiffGenres
Title
Kentucky Fried Movie, The (1977)2.8787883.5551470.676359Comedy
Jumpin' Jack Flash (1986)3.2547172.5783580.676359Action|Comedy|Romance|Thriller
Longest Day, The (1962)3.4117654.0314470.619682Action|Drama|War
Cable Guy, The (1996)2.2500002.8637870.613787Comedy
For a Few Dollars More (1965)3.4090913.9537950.544704Western
Porky's (1981)2.2968752.8363640.539489Comedy
Fright Night (1985)2.9736843.5000000.526316Comedy|Horror
Anastasia (1997)3.8000003.2816090.518391Animation|Children's|Musical
French Kiss (1995)3.5357143.0569620.478752Comedy|Romance
Little Shop of Horrors, The (1960)3.6500003.1796880.470312Comedy|Horror
算出每部电影平均得分并对其进行排序
#算出每部电影平均得分并对其进行排序 data_mean_rating 接收
data_rating_num = data_rating_num[data_rating_num>100]
mask = data['Title'].apply(lambda x: True if x in data_rating_num.index else False)
data_mean_rating = data[mask].pivot_table(index='Title', values=['Rating'])
data_mean_rating
Rating
Title
'burbs, The (1989)2.910891
...And Justice for All (1979)3.713568
10 Things I Hate About You (1999)3.422857
101 Dalmatians (1961)3.596460
101 Dalmatians (1996)3.046703
......
Young Guns II (1990)2.907859
Young Sherlock Holmes (1985)3.390501
Your Friends and Neighbors (1998)3.376147
Zero Effect (1998)3.750831
eXistenZ (1999)3.256098

2006 rows × 1 columns

# 对电影平均得分排序
data_mean_rating_sorted=data_mean_rating.sort_values(by='Rating',ascending=False)
data_mean_rating_sorted.head()
Rating
Title
Seven Samurai (The Magnificent Seven) (Shichinin no samurai) (1954)4.560510
Shawshank Redemption, The (1994)4.554558
Godfather, The (1972)4.524966
Close Shave, A (1995)4.520548
Usual Suspects, The (1995)4.517106
取评分数量最多的前20条数据
#对评分数量进行排序,并取前20条数据
hot_movies_sorted=data_rating_num.sort_values(ascending=False)
hot_movies_sorted[:20]
Title
American Beauty (1999)                                   3428
Star Wars: Episode IV - A New Hope (1977)                2991
Star Wars: Episode V - The Empire Strikes Back (1980)    2990
Star Wars: Episode VI - Return of the Jedi (1983)        2883
Jurassic Park (1993)                                     2672
Saving Private Ryan (1998)                               2653
Terminator 2: Judgment Day (1991)                        2649
Matrix, The (1999)                                       2590
Back to the Future (1985)                                2583
Silence of the Lambs, The (1991)                         2578
Men in Black (1997)                                      2538
Raiders of the Lost Ark (1981)                           2514
Fargo (1996)                                             2513
Sixth Sense, The (1999)                                  2459
Braveheart (1995)                                        2443
Shakespeare in Love (1998)                               2369
Princess Bride, The (1987)                               2318
Schindler's List (1993)                                  2304
L.A. Confidential (1997)                                 2288
Groundhog Day (1993)                                     2278
dtype: int64
查看不同年龄的分布情况并且采用直方图进行可视化
import matplotlib.pyplot as plt
users.Age.plot.hist(bins=10, edgecolor='white')
plt.title('users_ages')
plt.xlabel('age')
plt.ylabel('count of age')
xticks = np.linspace(np.min(users.Age), np.max(users.Age), 11)
plt.xticks(xticks)
plt.show()

在这里插入图片描述

每10岁一个区间,统计出用户的年龄分组分布
data['Age'].plot(kind='hist',bins=10)
plt.xticks(rotation=45)
plt.show()

在这里插入图片描述

统计数据集中每一类型的电影频数
df = pd.DataFrame(movies.Genres.str.split('|').tolist())
df = df.stack().reset_index()
df = df.drop(['level_0', 'level_1'], axis=1)
genres = df.groupby(0).size()
genres.sort_values(ascending=False).plot(kind='bar')
# movies_ratings_sorted.
plt.xticks(rotation=45)
plt.show()

在这里插入图片描述

项目四:二手房源信息数据分析与可视化

二手房源信息数据分析与可视化
# 导入模块
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 设置使中文显示完整
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False

#所有房源信息
house=pd.read_csv('house.csv')
house.head(1)
indextitlecommunityyearshousetypesquarefloortaxtypetotalPriceunitPricefollowInfo
00宝星华庭一层带花园,客厅挑高,通透四居室。房主自荐宝星国际三期底层(共22层)2010年建板塔结合4室1厅298.79平米底层(共22层)2010年建板塔结合距离15号线望京东站680米房本满五年25988695153人关注 / 共44次带看 / 一年前发布
数据描述性分析
house.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 16108 entries, 0 to 16107
Data columns (total 11 columns):
index         16108 non-null int64
title         16108 non-null object
community     16108 non-null object
years         16106 non-null object
housetype     16108 non-null object
square        16108 non-null object
floor         16106 non-null object
taxtype       15361 non-null object
totalPrice    16108 non-null int64
unitPrice     16108 non-null int64
followInfo    16108 non-null object
dtypes: int64(3), object(8)
memory usage: 1.4+ MB
# 所有小区信息
community=pd.read_csv('community_describe.csv')
community.head()
indexidcommunitydistrictbizcircletagListonsale
001111000004310什坊院甲3号院海淀田村NaN0
111111027373682大慧寺6号院海淀白石桥NaN2
221111027373683东花市北里东区东城东花市近地铁1号线王府井站0
331111027373684东花市北里西区东城东花市近地铁7号线广渠门内站7
441111027373685东花市北里中区东城东花市近地铁2号线朝阳门站9
# 合并小区信息和房源信息表,可以获得房源更详细的地理位置
house_detail=pd.merge(house,community,on='community')
# 打印数据
house_detail.head(1)
# len(house_detail)
index_xtitlecommunityyearshousetypesquarefloortaxtypetotalPriceunitPricefollowInfoindex_yiddistrictbizcircletagListonsale
00宝星华庭一层带花园,客厅挑高,通透四居室。房主自荐宝星国际三期底层(共22层)2010年建板塔结合4室1厅298.79平米底层(共22层)2010年建板塔结合距离15号线望京东站680米房本满五年25988695153人关注 / 共44次带看 / 一年前发布15351111027376204朝阳望京近地铁15号线望京东站7
数值型数据描述
house.describe()
indextotalPriceunitPrice
count16108.00000016108.00000016108.000000
mean8053.500000747.98373577656.823814
std4650.123403536.20230623616.114546
min0.00000015.0000002539.000000
25%4026.750000439.00000060449.500000
50%8053.500000600.00000075094.000000
75%12080.250000870.00000091474.250000
max16107.00000012500.000000159991.000000
数据预处理1:将数据从字符串提取出来
# 将字符串转换成数字
def data_ad(select_data,str):
    if str in select_data:
       return float(select_data[0:select_data.find(str)])
    else:
       return None

# 处理房屋面积数据
house['square']=house['square'].apply(data_ad,str='平米')
# 查看数据
house.head(1)
indextitlecommunityyearshousetypesquarefloortaxtypetotalPriceunitPricefollowInfo
00宝星华庭一层带花园,客厅挑高,通透四居室。房主自荐宝星国际三期底层(共22层)2010年建板塔结合4室1厅298.79底层(共22层)2010年建板塔结合距离15号线望京东站680米房本满五年25988695153人关注 / 共44次带看 / 一年前发布
house.describe()
indexsquaretotalPriceunitPriceattention
count16026.00000016026.00000016026.00000016026.00000016026.000000
mean8061.29071595.997246743.13602977796.26887658.154936
std4648.83672057.606275510.15595623441.07045968.642351
min0.00000015.29000040.00000011393.0000000.000000
25%4037.25000061.110000440.00000060589.25000017.000000
50%8066.50000081.200000599.00000075184.50000037.000000
75%12085.750000112.757500870.00000091516.00000073.000000
max16107.0000002623.28000012000.000000159991.0000001401.000000
house[house['square']<16]
indextitlecommunityyearshousetypesquarefloortaxtypetotalPriceunitPricefollowInfoattentionlayeryear
1526015260智德北巷(北河沿大街)+小户型一居+南向智德北巷中楼层(共6层)1985年建板楼1室0厅15.29中楼层(共6层)1985年建板楼距离5号线灯市口站1113米22014388556人关注 / 共2次带看 / 8天以前发布56.0中楼层1985年
户型的种类
house.housetype.value_counts()
2室1厅     6582
3室1厅     2534
1室1厅     2472
3室2厅     1424
2室2厅     1018
1室0厅      620
4室2厅      496
4室1厅      181
2房间1卫     100
5室2厅       92
1房间1卫      87
1室2厅       64
4室3厅       55
3房间1卫      44
3室0厅       35
2室0厅       34
车位         32
6室2厅       29
5室3厅       22
联排别墅       19
1房间0卫      16
5室1厅       15
6室3厅       13
独栋别墅       12
3室3厅       11
4室0厅       10
叠拼别墅       10
双拼别墅        9
4房间2卫       9
4房间1卫       6
2房间2卫       6
6室1厅        5
5室4厅        4
6室4厅        3
7室3厅        3
5室5厅        3
3房间2卫       3
5房间3卫       2
2室3厅        2
9室4厅        2
6房间4卫       2
2房间0卫       2
6房间2卫       2
3房间3卫       2
4房间3卫       2
7室2厅        2
8室2厅        1
5室0厅        1
6室0厅        1
2房间3卫       1
4室4厅        1
5房间2卫       1
7室0厅        1
8房间5卫       1
3室4厅        1
8室4厅        1
6房间3卫       1
7室1厅        1
Name: housetype, dtype: int64
数据预处理2:删除车位信息
car=house[house.housetype.str.contains('车位')]
# 记录中共有车位
car.shape[0]
# 删除车位信息
house.drop(car.index,inplace=True)
# 现在还剩?条记录
car.shape
(32, 11)
数据分析1:价格最高的5个别墅
villa=house[house.housetype.str.contains('别墅')]
# 记录中共有别墅?
villa.shape[0]
# 排序
villa.sort_values(by='totalPrice',ascending=False).head(5)
indextitlecommunityyearshousetypesquarefloortaxtypetotalPriceunitPricefollowInfo
80208020香山清琴二期独栋别墅,毛坯房原始户型,花园1200平米香山清琴2层2007年建独栋别墅NaN2层2007年建房本满五年1250012468145人关注 / 共7次带看 / 2个月以前发布
102102千尺独栋 北入户 红顶商人金融界入住社区龙湖颐和原著2层2010年建独栋别墅NaN2层2010年建距离4号线西苑站839米房本满五年12000112012231人关注 / 共26次带看 / 一年前发布
27292729临湖独栋别墅 花园半亩 观景湖面和绿化 满五年有车库房主自荐紫玉山庄3层2000年建独栋别墅NaN3层2000年建房本满五年6000148618108人关注 / 共16次带看 / 5个月以前发布
31413141银湖别墅 独栋 望京公园旁 五环里 封闭式社区银湖别墅3层1998年建独栋别墅NaN3层1998年建房本满五年50001303489人关注 / 共3次带看 / 5个月以前发布
41124112首排别墅 位置好 全景小区绿化和人工湖 有车库亚运新新家园朗月园一期1层2003年建联排别墅NaN1层2003年建房本满五年3800823640人关注 / 共4次带看 / 4个月以前发布
数据预处理3:删除别墅信息
house.drop(villa.index,inplace=True)
# 现在还剩下?条记录
house.shape[0]
16026
数据分析2:找出数据中的住房户型分布
# 户型分布
house.housetype.value_counts()
2室1厅     6582
3室1厅     2534
1室1厅     2472
3室2厅     1424
2室2厅     1018
1室0厅      620
4室2厅      496
4室1厅      181
2房间1卫     100
5室2厅       92
1房间1卫      87
1室2厅       64
4室3厅       55
3房间1卫      44
3室0厅       35
2室0厅       34
6室2厅       29
5室3厅       22
1房间0卫      16
5室1厅       15
6室3厅       13
3室3厅       11
4室0厅       10
4房间2卫       9
2房间2卫       6
4房间1卫       6
6室1厅        5
5室4厅        4
6室4厅        3
3房间2卫       3
7室3厅        3
5室5厅        3
5房间3卫       2
2室3厅        2
9室4厅        2
6房间4卫       2
2房间0卫       2
6房间2卫       2
3房间3卫       2
4房间3卫       2
7室2厅        2
8室2厅        1
5室0厅        1
6室0厅        1
2房间3卫       1
4室4厅        1
5房间2卫       1
7室0厅        1
8房间5卫       1
3室4厅        1
8室4厅        1
6房间3卫       1
7室1厅        1
Name: housetype, dtype: int64
# 可视化绘制
house_type=house.housetype.value_counts()
house_type.head(10).plot(kind='bar',title='户型数量分布',rot=30)
# plt.show()
<matplotlib.axes._subplots.AxesSubplot at 0x12dae357a90>

在这里插入图片描述

数据分析3:找出关注人数最多的五套房子
house['attention']=house['followInfo'].apply(data_ad,str='人关注')
house.head(5)
house.sort_values(by='attention',ascending=False).head()
indextitlecommunityyearshousetypesquarefloortaxtypetotalPriceunitPricefollowInfoattention
4747弘善家园南向开间,满两年,免增值税弘善家园中楼层(共28层)2009年建塔楼1室0厅42.64中楼层(共28层)2009年建塔楼距离10号线十里河站698米房本满两年随时看房265621491401人关注 / 共305次带看 / 一年前发布1401.0
23132313四惠东 康家园 南向一居室 地铁1号线出行房主自荐康家园顶层(共6层)1995年建板楼1室1厅41.97顶层(共6层)1995年建板楼距离1号线四惠东站974米房本满五年随时看房262624261005人关注 / 共86次带看 / 6个月以前发布1005.0
990990远见名苑 东南两居 满五年家庭唯一住房 诚心出售房主自荐远见名苑中楼层(共24层)2004年建塔楼2室1厅90.14中楼层(共24层)2004年建塔楼距离7号线达官营站516米房本满五年81189972979人关注 / 共50次带看 / 8个月以前发布979.0
23312331荣丰二期朝南复式无遮挡全天采光房主自荐荣丰2008中楼层(共10层)2005年建塔楼1室1厅32.54中楼层(共10层)2005年建塔楼距离7号线达官营站1028米房本满五年随时看房400122926972人关注 / 共369次带看 / 6个月以前发布972.0
915915通州万达北苑地铁站 天时名苑 大两居可改3居天时名苑顶层(共9层)2009年建板塔结合2室2厅121.30顶层(共9层)2009年建板塔结合距离八通线通州北苑站602米房本满五年64553174894人关注 / 共228次带看 / 8个月以前发布894.0
数据分析4:户型和关注人数分布
#取户型>50的数据进行可视化
type_interest_group=house.groupby(house['housetype']).agg({'housetype':'count','attention':'sum'})

interest_sort=type_interest_group[type_interest_group['housetype']>50]
interest_sort.plot(kind='barh',title='二手房户型和关注人数分布', y='attention')
interest_sort
housetypeattention
housetype
1室0厅62032920.0
1室1厅2472141893.0
1室2厅642614.0
1房间1卫872267.0
2室1厅6582394987.0
2室2厅101849526.0
2房间1卫1003006.0
3室1厅2534162205.0
3室2厅142481140.0
4室1厅18110667.0
4室2厅49630661.0
4室3厅552846.0
5室2厅924703.0

在这里插入图片描述

数据分析5:面积分布
# 面积分布
area_level=[0,50,100,150,200,250,300,350,400,450,500]
label_level=['小于50','50-100','100-150','150-200','200-250','250-300','300-350','350-400','400-450','450-500']
area_cut=pd.cut(house['square'],bins=area_level,labels=label_level)
area_cut.value_counts()[::-1].plot(kind='barh', title='二手房面积分布',fontsize='small')
<matplotlib.axes._subplots.AxesSubplot at 0x12dae064a90>

在这里插入图片描述

数据分析6:各个行政区房源单价均价
house_unitPrice=house_detail.groupby('district')['unitPrice'].mean()
house_unitPrice.plot(kind='barh', title='各个行政区房源均价')
# agg({'unitPrice':'mean'})
<matplotlib.axes._subplots.AxesSubplot at 0x12dae19c7f0>

在这里插入图片描述

各个行政区房源价钱箱线图绘制

import seaborn as sns
price=house_detail[['district','unitPrice']]
price.boxplot(by='district', grid=0)
<matplotlib.axes._subplots.AxesSubplot at 0x12dae1ede80>

在这里插入图片描述

各个行政区房源在售数量

house_onsale=house_detail.groupby('district')['onsale'].count()
house_onsale.plot(kind='bar',rot=30,title='各个行政区房源在售数量')
<matplotlib.axes._subplots.AxesSubplot at 0x12dae3c9fd0>

在这里插入图片描述

数据分析7:各个行政区的房源总价对比
price=house_detail[['district','totalPrice']]
sns.boxplot(x='district',y='totalPrice',data=price)
plt.ylim((0,6000))
(0, 6000)

在这里插入图片描述

通过箱型图看到,各大区域房屋总价中位数都都在1000万以下,且房屋总价离散值较高

数据分析8:按照地铁信息对各个区域每平米均价排序,柱形图绘制
bizcircle_unitPrice=house_detail.groupby('bizcircle')['unitPrice'].mean().sort_values(ascending=False)
bizcircle_unitPrice.head(15).plot(kind='bar',title='各个区域均价分布',rot=30)
plt.legend(['均价'])
# plt.show()
<matplotlib.legend.Legend at 0x12daceabc50>

在这里插入图片描述

数据分析9:按小区均价排序
community_unitPrice=house_detail.groupby('community')['unitPrice'].mean().sort_values(ascending=False)
community_unitPrice.head(10).plot(kind='bar',title='各个小区均价分布',rot=30)
plt.legend(['均价'])
<matplotlib.legend.Legend at 0x12dadf03898>

在这里插入图片描述

数据分析10: 楼层的分布情况
# 将字符串转换成数字
def data_ads(select_data,str):
    if str in select_data:
       return (select_data[0:select_data.find(str)])
    else:
       return '没有提取到楼层信息'
# 得到楼层
# 将字符串转换成数字
house['layer']=house['years'].apply(data_ads,str='(')
house.head(3)
indextitlecommunityyearshousetypesquarefloortaxtypetotalPriceunitPricefollowInfoattentionlayer
00宝星华庭一层带花园,客厅挑高,通透四居室。房主自荐宝星国际三期底层(共22层)2010年建板塔结合4室1厅298.79底层(共22层)2010年建板塔结合距离15号线望京东站680米房本满五年25988695153人关注 / 共44次带看 / 一年前发布53.0底层
11三面采光全明南北朝向 正对小区绿地花园顶秀青溪中楼层(共11层)2008年建板塔结合3室2厅154.62中楼层(共11层)2008年建板塔结合距离5号线立水桥站1170米房本满两年随时看房100064675323人关注 / 共579次带看 / 一年前发布323.0中楼层
22沁园公寓 三居室 距离苏州街地铁站383米沁园公寓低楼层(共24层)1999年建塔楼3室2厅177.36低楼层(共24层)1999年建塔楼距离10号线苏州街站383米房本满五年120067659185人关注 / 共108次带看 / 一年前发布185.0低楼层
# 楼层分布及可视化
house['layer'].value_counts().plot(kind='bar',rot=30)
<matplotlib.axes._subplots.AxesSubplot at 0x12dae010da0>

在这里插入图片描述

数据分析11:绘制2000到2016平均房价(年份与总售价的可视化)
# 得到年份
def data_adst(select_data,str):
    if str in select_data:
        return (select_data[select_data.find(str)-5:select_data.find(str)])
    else:
        return None
house['year']=house['years'].apply(data_adst,str='建')
house.head(4)
indextitlecommunityyearshousetypesquarefloortaxtypetotalPriceunitPricefollowInfoattentionlayeryear
00宝星华庭一层带花园,客厅挑高,通透四居室。房主自荐宝星国际三期底层(共22层)2010年建板塔结合4室1厅298.79底层(共22层)2010年建板塔结合距离15号线望京东站680米房本满五年25988695153人关注 / 共44次带看 / 一年前发布53.0底层2010年
11三面采光全明南北朝向 正对小区绿地花园顶秀青溪中楼层(共11层)2008年建板塔结合3室2厅154.62中楼层(共11层)2008年建板塔结合距离5号线立水桥站1170米房本满两年随时看房100064675323人关注 / 共579次带看 / 一年前发布323.0中楼层2008年
22沁园公寓 三居室 距离苏州街地铁站383米沁园公寓低楼层(共24层)1999年建塔楼3室2厅177.36低楼层(共24层)1999年建塔楼距离10号线苏州街站383米房本满五年120067659185人关注 / 共108次带看 / 一年前发布185.0低楼层1999年
33金星园东南向户型,四居室设计,中间楼层金星园中楼层(共28层)2007年建塔楼4室2厅245.52中楼层(共28层)2007年建塔楼距离机场线三元桥站1153米房本满五年165067205157人关注 / 共35次带看 / 一年前发布157.0中楼层2007年
# 绘制2000到2016平均房价
data=house.groupby('year').agg({'totalPrice':'mean'})
data
data['2000年':'2016年'].plot(kind='bar',rot=30)
<matplotlib.axes._subplots.AxesSubplot at 0x12dae301f98>

在这里插入图片描述

综合:紧邻望京地铁站,三室一厅,400万-500万,大于80平米的房子
第一步:找出望京附近的房屋信息
myhouse=house_detail[house_detail.bizcircle.str.contains('望京')]
# myhouse.head(2)
len(myhouse)
896
第二步:查看分布情况
house_type=myhouse['housetype'].value_counts()
house_type.head(10).plot(kind='bar',title='户型数量分布',rot=30)
house_type.head(10)
2室1厅     230
3室2厅     155
2室2厅     134
1室1厅     117
3室1厅     108
4室2厅      55
1室0厅      25
4室1厅      25
2房间1卫     13
5室2厅       8
Name: housetype, dtype: int64

在这里插入图片描述

第三步:找到三室一厅的房源信息以及400万-500万,大于80平米的房源信息
# 1 找到三室一厅的房源信息
myhouse=myhouse[myhouse.housetype.str.contains('3室1厅')]
len(myhouse)
108
# 2 房屋总价400万-500万之间
myhouse=myhouse.loc[(myhouse['totalPrice']>400)&(myhouse['totalPrice']<500)]
myhouse.head()
len(myhouse)
7
# 将字符串转换成数字
# def data_ad(select_data,str):
#     if str in select_data:
#        return float(select_data[0:select_data.find(str)])
#     else:
#        return None

# 处理房屋面积数据
myhouse['square']=myhouse['square'].apply(data_ad,str='平米')
# 3 房屋面积大于80平米
myhouse=myhouse.loc[myhouse.square>80]
len(myhouse)
myhouse.head()
index_xtitlecommunityyearshousetypesquarefloortaxtypetotalPriceunitPricefollowInfoindex_yiddistrictbizcircletagListonsale
78242806花家地西里一区东西向三居室 中间楼层 带电梯房主自荐花家地西里一区中楼层(共12层)1997年建板塔结合3室1厅82.10中楼层(共12层)1997年建板塔结合房本满五年随时看房48058466245人关注 / 共75次带看 / 5个月以前发布8201111027375067朝阳望京近地铁14号线(东段)阜通站8
140228669经典三居室格局合理社区安静配套成熟房主自荐中环南路5号院顶层(共6层)1996年建板楼3室1厅88.51顶层(共6层)1996年建板楼距离14号线(东段)望京南站701米房本满两年4955592635人关注 / 共0次带看 / 2个月以前发布47181111027382477朝阳望京近地铁14号线(东段)望京南站1

项目五:电信流失用户数据分析与可视化

手机客户流失预测
# 导入分析用到的模块
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
# %matplotlib inline 可以在Ipython编译器里直接使用,功能是可以内嵌绘图,并且可以省略掉plt.show()这一步。 
plt.style.use('ggplot')
import seaborn as sns
sns.set_style('darkgrid')
sns.set_palette('muted')
# 导入csv文件
df = pd.read_excel('CustomerSurvival.xlsx',encoding='utf-8')
df.head()
ID套餐金额额外通话时长额外流量改变行为服务合约关联购买集团用户使用月数流失用户
011792.833333-10.4500670000250
121121.666667-21.1411170000250
231-30.000000-25.655273000021
341241.500000-288.3412540101250
4511629.666667-23.6555050001250
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4975 entries, 0 to 4974
Data columns (total 10 columns):
ID        4975 non-null int64
套餐金额      4975 non-null int64
额外通话时长    4975 non-null float64
额外流量      4975 non-null float64
改变行为      4975 non-null int64
服务合约      4975 non-null int64
关联购买      4975 non-null int64
集团用户      4975 non-null int64
使用月数      4975 non-null int64
流失用户      4975 non-null int64
dtypes: float64(2), int64(8)
memory usage: 388.8 KB
df.columns = ['id','pack_type','extra_time','extra_flow','pack_change',
             'contract','asso_pur','group_user','use_month','loss']
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4975 entries, 0 to 4974
Data columns (total 10 columns):
id             4975 non-null int64
pack_type      4975 non-null int64
extra_time     4975 non-null float64
extra_flow     4975 non-null float64
pack_change    4975 non-null int64
contract       4975 non-null int64
asso_pur       4975 non-null int64
group_user     4975 non-null int64
use_month      4975 non-null int64
loss           4975 non-null int64
dtypes: float64(2), int64(8)
memory usage: 388.8 KB

id – 用户的唯一标识

pack_type – 用户的月套餐的金额,1为96元以下,2为96到225元,3为225元以上

extra_time – 用户在使用期间的每月额外通话时长,这部分需要用户额外交费。数值是每月的额外通话时长的平均值,单位:分钟

extra_flow – 用户在使用期间的每月额外流量,这部分需要用户额外交费。数值是每月的额外流量的平均值,单位:兆

pack_change – 是否曾经改变过套餐金额,1=是,0=否

contract – 用户是否与联通签订过服务合约,1=是,0=否

asso_pur – 用户在使用联通移动服务过程中是否还同时办理其他业务,1=同时办理一项其他业务,2=同时办理两项其他业务,0=没有办理其他业务

group_use – 用户办理的是否是集团业务,相比个人业务,集体办理的号码在集团内拨打有一定优惠。1=是,0=否

use_month – 截止到观测期结束(2012.1-2014.1),用户使用联通服务的时间长短,单位:月

loss – 在25个月的观测期内,用户是否已经流失。1=是,0=否

【数据的探索性分析】
df.describe()
idpack_typeextra_timeextra_flowpack_changecontractasso_purgroup_useruse_monthloss
count4975.0000004975.0000004975.0000004975.0000004975.0000004975.0000004975.0000004975.0000004975.0000004975.000000
mean2488.0000001.057688258.520030-71.5804030.0213070.2452260.0474370.22733714.7742710.782714
std1436.3031250.258527723.057190275.5574480.1444190.4302640.2781430.4191546.5342730.412441
min1.0000001.000000-2828.333333-2189.8759860.0000000.0000000.0000000.0000001.0000000.000000
25%1244.5000001.000000-126.666667-74.2898240.0000000.0000000.0000000.00000013.0000001.000000
50%2488.0000001.00000013.500000-59.6527340.0000000.0000000.0000000.00000013.0000001.000000
75%3731.5000001.000000338.658333-25.7950450.0000000.0000000.0000000.00000019.0000001.000000
max4975.0000003.0000004314.0000002568.7042931.0000001.0000002.0000001.00000025.0000001.000000

可以看到extra_time和extra_flow有正负值,正数表示用户有额外的通话时长和流量,负数为用户在月底时剩余的套餐时长和流量。从四分位数中可看出超过一半的用户有额外通话时间,流量的话只有小部分用户超额使用了。另外其他的分类型变量在描述统计上并未发现有异常的地方。



在这里特别注意下use_month这个变量,数据的观测区间为2012.1-2014.1,一共25个月,且案例中关于流失的定义为:


超过一个月没有使用行为(包括通话,使用流量)的用户判定为流失。


在数据集中use_month小于25个月的基本都是流失状态,所以这个变量对于流失的预测并没有什么关键作用,后续导入模型时需剔除这个变量。

2. 变量的分布
# 首先看一下两个连续型变量:extra_time和extra_flow的数据分布:
plt.figure(figsize = (10,5))
plt.subplot(121)
df.extra_time.hist(bins = 30)
plt.subplot(122)
df.extra_flow.hist(bins = 30)
<matplotlib.axes._subplots.AxesSubplot at 0x1594afe2cf8>

在这里插入图片描述

extra_time呈现的是右偏分布,extra_flow近似服从正态分布,与描述统计中的情况大致吻合

# 接下来看看分类型变量的分布:
# 以bar的形式展示每个类别的数量
# 金额  是否曾经改变过套餐金额    用户是否与联通签订过服务合约  
# 用户在使用联通移动服务过程中是否还同时办理其他业务   用户办理的是否是集团业务   流失
fig,axes = plt.subplots(nrows = 2,ncols = 3, figsize = (10,6))
sns.countplot(x = 'pack_type',data = df,ax=axes[0,0])
sns.countplot(x = 'pack_change',data = df,ax=axes[0,1])
sns.countplot(x = 'contract',data = df,ax=axes[0,2])
sns.countplot(x = 'asso_pur',data = df,ax=axes[1,0])
sns.countplot(x = 'group_user',data = df,ax=axes[1,1])
sns.countplot(x = 'loss',data = df,ax=axes[1,2])
<matplotlib.axes._subplots.AxesSubplot at 0x1594b1aa240>

在这里插入图片描述

可以看到pack_type, pack_change, asso_pur的类型分布非常不均衡,例如asso_pur,办理过套餐外业务的用户数量极少,导致样本缺乏足够的代表性,可能会对模型的最终结果产生一定的影响。

3. 自变量与因变量之间的关系:
# 对于extra_time和extra_flow绘制散点图观察:
plt.figure(figsize = (10,6))
df.plot.scatter(x='extra_time',y='loss')
df.plot.scatter(x='extra_flow',y='loss')
<matplotlib.axes._subplots.AxesSubplot at 0x1594b23de80>




<Figure size 720x432 with 0 Axes>

在这里插入图片描述

在这里插入图片描述

从散点图上似乎感觉两个自变量与是否流失并无关系,为了更好的展示其相关性,我们对extra_time和extra_flow进行分箱处理,再绘制条形图:

# 增加分箱后的两个字段
# 将连续性数据离散化
bin1 = [-3000,-2000,-500,0,500,2000,3000,5000]
df['time_label'] = pd.cut(df.extra_time,bins = bin1)
# 观察一下分箱后的数据分布
time_amount = df.groupby('time_label').id.count().sort_values().reset_index()
time_amount
time_amount['amount_cumsum'] = time_amount.id.cumsum()
time_amount

time_labelidamount_cumsum
0(-3000, -2000]33
1(-2000, -500]1518
2(3000, 5000]7997
3(2000, 3000]129226
4(500, 2000]755981
5(0, 500]16342615
6(-500, 0]23604975
sns.countplot(x = 'time_label',hue = 'loss',data =df)
# ---对extra_time进行累加统计,发现【-500,500】这个区间的用户占了80%,符合二八定律
# hue可以返回每一个区间的loss
<matplotlib.axes._subplots.AxesSubplot at 0x1594b3a1d68>

在这里插入图片描述

bin2 = [-3000,-2000,-500,0,500,2000,3000]
df['flow_label'] = pd.cut(df.extra_flow,bins = bin2)
flow_amount = df.groupby('flow_label').id.count().sort_values().reset_index()
flow_amount['amount_cumsum'] = flow_amount.id.cumsum()
flow_amount
flow_labelidamount_cumsum
0(2000, 3000]11
1(-3000, -2000]34
2(500, 2000]7983
3(-2000, -500]157240
4(0, 500]8271067
5(-500, 0]39084975

—对extra_flow进行累加统计,发现【-500,500】占了95%,且(-500,0】的用户占80%,可以说只有小部分用户每月会超额使用流量。

sns.countplot(x = 'flow_label',hue = 'loss',data =df)
# 使用bars来表示每个分类数据的数目
<matplotlib.axes._subplots.AxesSubplot at 0x1594b2abd30>

在这里插入图片描述

可以明显的看出用户使用的通话时间和流量越多,流失概率越低,这些超额使用的用户在用户分类中属于’高价值用户’,用户粘性很高,运营商应该把重点放在这些用户身上,采取有效的手段预防其流失。

fig,axes = plt.subplots(nrows = 2,ncols = 3, figsize = (12,8))
sns.countplot(x = 'pack_type',hue = 'loss',data =df,ax = axes[0][0])
sns.countplot(x = 'pack_change',hue = 'loss',data =df,ax = axes[0][1])
sns.countplot(x = 'contract',hue = 'loss',data =df,ax = axes[0][2])
sns.countplot(x = 'asso_pur',hue = 'loss',data =df,ax = axes[1][0])
sns.countplot(x = 'group_user',hue = 'loss',data =df,ax = axes[1][1])
<matplotlib.axes._subplots.AxesSubplot at 0x1594ad745c0>

在这里插入图片描述

初步得出以下结论:

1).套餐金额越大,用户越不易流失,套餐金额大的用户忠诚度也高

2).改过套餐的用户流失的概率变小

3).签订过合约的流失比例较小,签订合约也意味着一段时间内(比如2年,3年)用户一般都不会更换运营商号码,可以说签订合约的用户比较稳定

4).办理过其它套餐业务的用户因样本量太少,后续再研究

5).集团用户的流失率相比个人用户低很多

internal_chars = ['extra_time','extra_flow','pack_type',
                 'pack_change','contract','asso_pur','group_user','loss']
corrmat = df[internal_chars].corr()
f, ax = plt.subplots(figsize=(10, 7))
plt.xticks(rotation='0')
sns.heatmap(corrmat, square=False, linewidths=.5, annot=True)
<matplotlib.axes._subplots.AxesSubplot at 0x1594b332470>

在这里插入图片描述

各自变量之间的相关性程度很低,排除了共线性问题。在对因变量的相关性上contract和group_user的系数相比其它变量较高,但也不是很强。

数据建模

因为自变量大多数为分类型,所以用决策树的效果比较好,而且决策树对异常值的敏感度很低,生成的结果也有很好的解释性。

—因变量是 ‘loss’,是否流失,也是我们预测的目标值

—自变量分为三类:

#连续型变量:extra_time,extra_flow, use_month

#二元分类变量:pack_change,contract, group_use

#多元分类变量:pack_type,asso_pur

根据前面的探索性分析,并基于业务理解,我们决定筛选这几个特征进入模型:

extra_time,extra_flow,pack_type, pack_change, asso_pur

contract以及group_use,这些特征都对是否流失有一定的影响。

对于extra_time,extra_flow这两个连续型变量我们作数据转换,变成二分类变量,这样所有特征都是统一的度量。

df['time_tranf'] = df.apply(lambda x:1 if x.extra_time>0 else 0,axis =1)
df['flow_tranf'] = df.apply(lambda x:1 if x.extra_flow>0 else 0,axis =1)
df.head()
# 将没有超出套餐的通话时间和流量记为0,超出的记为1。
idpack_typeextra_timeextra_flowpack_changecontractasso_purgroup_useruse_monthlosstime_labelflow_labeltime_tranfflow_tranf
011792.833333-10.4500670000250(500, 2000](-500, 0]10
121121.666667-21.1411170000250(0, 500](-500, 0]10
231-30.000000-25.655273000021(-500, 0](-500, 0]00
341241.500000-288.3412540101250(0, 500](-500, 0]10
4511629.666667-23.6555050001250(500, 2000](-500, 0]10
x = df.loc[:,['pack_type','time_tranf','flow_tranf','pack_change','contract','asso_pur','group_user']]
x = np.array(x)
x
array([[1, 1, 0, ..., 0, 0, 0],
       [1, 1, 0, ..., 0, 0, 0],
       [1, 0, 0, ..., 0, 0, 0],
       ...,
       [1, 1, 1, ..., 1, 0, 0],
       [1, 1, 1, ..., 1, 0, 0],
       [3, 0, 0, ..., 1, 0, 1]], dtype=int64)
y = df.loss
y = y[:, np.newaxis]
y
array([[0],
       [0],
       [1],
       ...,
       [0],
       [1],
       [0]], dtype=int64)
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.3,random_state=123)
from sklearn import tree
clf = tree.DecisionTreeClassifier(criterion='gini', #--设置衡量的系数
                                    splitter='best', #--选择分类的策略
                                    max_depth=4, #--设置树的最大深度
                                    min_samples_split=10,#--节点的最少样本数
                                    min_samples_leaf=5 #-- 叶节点的最少样本数
                                    )
clf = clf.fit(x_train,y_train) # -- 拟合训练

这里我们采用决策树中ID3算法,基于entropy系数进行分类,设置树的最大深度为4,区分一个内部节点需要的最少的样本数为10,一个叶节点所需要的最小样本数为5。

train_score = clf.score(x_train,y_train) # 训练集的评分
test_score = clf.score(x_test,y_test)   # 测试集的评分
'train_score:{0},test_score:{1}'.format(train_score,test_score)
'train_score:0.871338311315336,test_score:0.8640321500334897'
参数调优
# 模型的参数调优--max_depth
# 创建一个函数,使用不同的深度来训练模型,并计算评分数据
def cv_score(d):
    clf2 = tree.DecisionTreeClassifier(max_depth=d)
    clf2 = clf2.fit(x_train,y_train)
    tr_score = clf2.score(x_train,y_train)
    cv_score = clf2.score(x_test,y_test)
    return (tr_score, cv_score)
# 构造参数范围,在这个范围内构造模型并计算评分
depths = range(2,15)
scores = [cv_score(d) for d in depths]
tr_scores = [s[0] for s in scores]
cv_scores = [s[1] for s in scores]
scores
# 找出交叉验证数据集最高评分的那个索引
best_score_index = np.argmax(cv_scores)
best_score = cv_scores[best_score_index]
best_param = depths[best_score_index]
best_param
# best_score
4
plt.figure(figsize = (4,2),dpi=150)
plt.grid()
plt.xlabel('max_depth')
plt.ylabel('best_score')
plt.plot(depths, cv_scores,'.g-',label = 'cross_validation scores')
plt.plot(depths,tr_scores,'.r--',label = 'train scores')
plt.legend()
<matplotlib.legend.Legend at 0x1594bb49518>

在这里插入图片描述

在生成的图中可以看出当深度为4时,交叉验证数据集的评分与训练集的评分比较接近,且两者的评分比较高,当深度超过5以后,俩者的差距变大,交叉验证数据集的评分变低,出现了过拟合情况。

模型结果评价
from sklearn.metrics import classification_report
y_pre = clf.predict(x_test)
print(classification_report(y_pre,y_test))
              precision    recall  f1-score   support

           0       0.65      0.71      0.68       304
           1       0.92      0.90      0.91      1189

    accuracy                           0.86      1493
   macro avg       0.79      0.81      0.80      1493
weighted avg       0.87      0.86      0.87      1493

精确率 = TP/(TP+FP) :在预测为流失的用户中,预测正确的(实际也是流失)用户占比

召回率 = TP/(TP+FN) : 在实际为流失的用户中,预测正确的(预测为流失的)用户占比

F1值为精确率和召回率的调和均值,相当于这两个的综合评价指标。

通过输出的分析报告可以得出建立的预测模型的精确率为0.88,说明在预测为流失的用户中,实际流失的用户占88%,召回率为0.86,说明实际为流失的用户中,预测为流失的占86%,F1值为0.87,说明模型的综合评价还不错。

# import os
# os.environ["PATH"] += os.pathsep + 'D:\新建文件夹\graphviz-2.38\release\bin'  #注意修改你的路径
# import os
#  os.environ["PATH"] += os.pathsep + 'C:/Program Files (x86)/Graphviz2.38/bin/'
from IPython.display import Image  
from sklearn import tree
import pydotplus 
from sklearn.tree import export_graphviz
def TreeShow(dtClass,irisDataSet):
    dot_data = export_graphviz(dtClass, out_file=None)
    graph = pydotplus.graph_from_dot_data(dot_data)
    graph.write_pdf("tree.pdf")
    dot_data = export_graphviz(dtClass, out_file=None,
                               feature_names=['pack_type','time_tranf','flow_tranf'
                               ,'pack_change','contract','asso_pur','group_user'],   #对应特征的名字
                              class_names=['loss','not loss'],    #对应类别的名字
                               filled=True, rounded=True,
                               special_characters=True)
    graph = pydotplus.graph_from_dot_data(dot_data)
    Image(graph.create_png())
TreeShow(clf,df)
  • 6
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YEGE学AI算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值