【Python数据分析】Pandas数据载入与预处理,看这一篇就够了!
对于数据分析而言,数据大部分来源于外部数据,例如CSV文件、Excel文件以及数据库文件等等。
我们要把各种格式的数据转换成Pandas可处理的Series和DataFrame数据格式,进行完数据分析与处理之后再重新存储到外部文件中,这就是Pandas的数据载入与预处理。
其实对于读/写文件和存储文件来说,不同类型文件的函数格式都差不多,遵循以下格式:
读/写文件:read_xxx
存储文件:to_xxx
区别主要还是在参数类型的不同上。
文本文件是一种由若干行字符构成的文件,同时它也是一种按照顺序来进行书写的文件。
文本文件分为普通的文本文件和CSV文件。
CSV文件是一种使用逗号分隔的文件格式,但因为有时候又不一定是逗号,所以也被称为 字符分隔文件 。
文本文件都以纯文本形式保存数据。
使用read_table()
来读取文本文件
使用read_csv()
来读取CSV文件
它们有如下的参数:
参数 | 说明 |
---|---|
filepath | 接受string,代表文件路径 |
sep | 接受string,代表分隔符。table默认为[Tab],csv默认为”,“ 。注意:如果分隔符指定错误,就会导致数据连成一片 |
header | 接受int或sequence,将某行数据作为列名,默认为infer,即自动识别 |
names | 接受array,表示列名,默认为None |
index_col | 接受int、sequence或False,表示索引列的位置,默认为None |
dtype | 接受dict,代表写入的数据类型(列名为key,数据格式为values),默认为None |
engine | 接受c或者python,代表你要使用编程语言引擎,默认为c |
nrows | 接受int,表示读取前n行,默认为None |
df1 = pd.read_csv('xxx文件路径名')
df2 = pd.read_table('xxx文件路径名',sep = ',')
df3 = pd.read_csv('xxx文件路径名',names = ['a','b','c'])
使用to_table
或者to_csv
进行存储。
Pandas允许读取xls和xlsx两种Excel文件。
使用read_excel
部分参数与文本文件的相同,以下只介绍多出来的部分(其实也只有一个)。
参数 | 说明 |
---|---|
sheetname | 接受string,int,表示Excel表内数据的分表位置,默认为0 |
使用to_excel
常用参数也基本一致,主要区别在于由于Excel是表格形式,没有sep
作为分隔符,并且多出了sheetname
来指定存储的Excel Sheet的名称,默认为sheet1。
JSON作为一种轻量级的数据交换格式,使用大括号来区分表示并存储。
使用read_json
由于有时候会出现顺序错乱的问题,我们要使用sort_index
来对行索引进行一下排序。
使用to_json
数据库是数据分析中很重要的一个部分。那么对它的读取也就显得格外值得重视。
对于数据库文件的读取,我们要注意,首先要进行数据库的连接connect()
,然后是读取read_sql
,最后还要进行关闭close()
。
import pandas as pd
import MySQLdb
conn = MySQLdb.connect(host = host,port = port,user = username,passwd = password,db = db_name)
df = pd.read_sql("select * from table_name",con = conn)
conn,close()
import pandas as pd
import pymssql
conn = pymssql.connect(host = host,port = port,user = username,passwd = password,db = db_name)
df = pd.read_sql("select * from table_name",con = conn)
conn,close()
如果有不同的数据来源,就要进行数据的合并。
merge
函数通过一个或多个键将两个DataFrame按行合并起来处理。它与SQL中的join
的用法类似。
格式就没必要写了,直接介绍参数。
参数 | 说明 |
---|---|
left | 参与合并的左侧DataFrame |
right | 参与合并的右侧DataFrame |
how | 连接方法:inner(默认),left,right,outer(这里需要自行去了解下几个合并方法的概念) |
on | 用于连接的列名 |
left_on | 左侧DataFrame中用于连接键的列 |
right_on | 右侧DataFrame中用于连接键的列 |
left_index | 左侧行索引作为连接键 |
right_index | 右侧行索引作为连接键 |
sort | 合并后会对数据进行排序,默认为Ture |
suffixes | 修改重复名 |
# merge合并数据
price = pd.DataFrame({'fruit':['apple','orange','pear','orange'],'price':[8,7,9,11]})
# print(price)
amount = pd.DataFrame({'fruit':['apple','orange','pear'],'amout':[5,11,8],'price':[8,7,9]})
# print(amount)
print(pd.merge(price,amount))
fruit price amout
0 apple 8 5
1 orange 7 11
2 pear 9 8
3 orange 11 11
由于两个DataFrame都由fruit列,所以默认会按照这个列来进行合并,并且根据默认的连接方法inner进行合并。
如果不想要根据默认的列来进行合并,可以进行指定
# merge合并数据
price = pd.DataFrame({'fruit':['apple','orange','pear'],'price':[8,7,9]})
# print(price)
amount = pd.DataFrame({'fruit':['apple','orange','pear'],'amout':[5,11,8],'price':[8,7,9]})
# print(amount)
print(pd.merge(price,amount,left_on='price',right_on='price'))
当你使用这样的代码进行输出你会发现有以下问题:
fruit_x price fruit_y amout
0 apple 8 apple 5
1 orange 7 orange 11
2 pear 9 pear 8
这里的fruit_x
和fruit_y
实际上就是重复的列,系统默认用_x 和 _y来区分,我们要消除的话,就可以使用suffixes
参数来进行消除重复列名。
# merge合并数据
price = pd.DataFrame({'fruit':['apple','orange','pear'],'price':[8,7,9]})
# print(price)
amount = pd.DataFrame({'fruit':['apple','orange','pear'],'amout':[5,11,8],'price':[8,7,9]})
# print(amount)
print(pd.merge(price,amount,left_on='price',right_on='price',suffixes=('_left','_right')))
fruit_left price fruit_right amout
0 apple 8 apple 5
1 orange 7 orange 11
2 pear 9 pear 8
如果两个DataFrame之间没有连接键,那么就无法使用merge方法。
# merge合并数据
price = pd.DataFrame({'fruit':['apple','orange','pear','orange'],'price':[8,7,9,11]})
amount = pd.DataFrame({'name':['apple','orange','pear'],'amout':[5,11,8]}) # 这样就会报错
print(pd.merge(price,amount))
那么我们就可以用concat
来进行连接。我们其实可以这样理解,merge就是合并,那么合并就必须得有相同的部分,也就是连接键;而concat就是连接,连接不一定需要相同的部分。
默认情况下会按照行的方向进行数据的堆叠,axis=1时就是列。
# concat连接数据
s1 = pd.Series([0,1],index = ['a','b'])
s2 = pd.Series([2,3,4],index = ['a','d','e'])
s3 = pd.Series([5,6],index = ['f','g'])
print(pd.concat([s1,s2,s3]))
# 只显示DataFrame的示例
data1 = pd.DataFrame(np.arange(6).reshape(2,3),columns=list('abc'))
print(data1)
data2 = pd.DataFrame(np.arange(20,26).reshape(2,3),columns=list('ayz'))
print(data2)
data = pd.concat([data1,data2],axis=0)
print(data)
a b c
0 0 1 2
1 3 4 5
a y z
0 20 21 22
1 23 24 25
a b c y z
0 0 1.0 2.0 NaN NaN
1 3 4.0 5.0 NaN NaN
0 20 NaN NaN 21.0 22.0
1 23 NaN NaN 24.0 25.0
如果axis=1,会有以下
a b c a y z
0 0 1 2 20 21 22
1 3 4 5 23 24 25
可以看出,concat默认为外连接,那么我们也可以指定join使用别的连接方式。
如果有重复的索引,就使用combine_first
data0 = data1.combine_first(data2)
print(data0)
注意,如果重复索引的数据不一样,会默认使用第一个DataFrame中的数据。
a b c a y z
0 0 1 2 20 21 22
1 3 4 5 23 24 25
a b c y z
0 0 1 2 21 22
1 3 4 5 24 25
数据一般时不完整、有噪声和不一致的。数据清洗包括填充缺失的数据值、光滑噪声等等。
确实,前面的操作我们发现虽然进行了数据的分析,但是总感觉有些看起来不够顺眼,而数据清洗就是让数据看起来更顺眼的。
isnull
来检测缺失值,通常是返回布尔值,如果是缺失值则返回True,否则返回Falseisnull().sum()
来统计缺失值,也可以使用info()
来查看DataFrame的缺失值dropna
来删除具有缺失值的行或列,其中参数:axis默认为0;how如果是’any’(默认),说明只要有缺失值就删除,如果是’all’,说明如果该行或者列全是缺失值在进行删除;thresh用于说明阙值;inplace说明是否对原数据进行返回。fillna
来填充缺失值,往往这个方法更常用,毕竟直接删除数据不太好。填充值一般是平均数、中位数或者众数之类较为稳定的数据,当然也可以指定一个常数,**更甚至可以用字典形式来实现不同列填充不同值。**inplace参数可以指定是返回新对象还是直接对原数据进行修改。如果有重复值,我们只需要保留一份即可,我们使用duplicates
可以进行判断是否有重复,使用drop_duplicates
进行重复值的删除。
# 检测与处理重复值
data = {
'name': ['Alice', 'Bob', 'Charlie', 'Alice', 'David', 'Alice'],
'age': [25, 30, 35, 25, 40, 25],
'salary': [5000, 7000, 8000, 5000, 9000, 6000]
}
df = pd.DataFrame(data)
print("原始DataFrame:")
print(df)
# 检测重复值(所有列)
duplicates = df.duplicated()
print("\n检测重复值(所有列):")
print(duplicates)
# 检测重复值(特定列 'name' 和 'age')
duplicates_subset = df.duplicated(subset=['name', 'age'])
print("\n检测重复值(特定列 'name' 和 'age'):")
print(duplicates_subset)
# 删除所有列的重复值
df_no_duplicates = df.drop_duplicates()
print("\n删除所有列的重复值后的DataFrame:")
print(df_no_duplicates)
# 删除特定列的重复值
df_no_duplicates_subset = df.drop_duplicates(subset=['name', 'age'])
print("\n删除特定列 'name' 和 'age' 的重复值后的DataFrame:")
print(df_no_duplicates_subset)
原始DataFrame:
name age salary
0 Alice 25 5000
1 Bob 30 7000
2 Charlie 35 8000
3 Alice 25 5000
4 David 40 9000
5 Alice 25 6000
检测重复值(所有列):
0 False
1 False
2 False
3 True
4 False
5 False
dtype: bool
检测重复值(特定列 'name' 和 'age'):
0 False
1 False
2 False
3 True
4 False
5 True
dtype: bool
删除所有列的重复值后的DataFrame:
name age salary
0 Alice 25 5000
1 Bob 30 7000
2 Charlie 35 8000
4 David 40 9000
5 Alice 25 6000
删除特定列 'name' 和 'age' 的重复值后的DataFrame:
name age salary
0 Alice 25 5000
1 Bob 30 7000
2 Charlie 35 8000
4 David 40 9000
异常值指的是数据中存在的个别数值明显偏离其余数据的值,异常值会严重干扰数据分析的结果。一般在庞大数据中异常值的发现可以通过图形来进行检测,那样更明显。
这里介绍三种方法
最显著的方法,看图说话。
箱线图使用数据中五个统计量(最小值、下四分位数Q1、中位数Q2、上四分位数Q3、最大值)来描述数据。
它可以粗略看出数据的对称性和分散程度等信息。
它如何检测异常值呢?这里有个公式:有min = Q1 - 1.5(Q3-Q1)
和max = Q3 + 1.5(Q3-Q1)
,那么小于min或者大于max的值被称为异常值。
异常值被定义为一组测定值中与平均值的偏差超过三倍标准差的值。(因为在正态分布中,这样的值出现的概率小于0.003)
使用replace
进行数据值的替换。
# 数据转换
data = {
'name': ['Alice', 'Bob', 'Charlie', 'Alice', 'David', '路人甲'],
'age': [25, 30, 35, 25, 40, 25],
'salary': [5000, 7000, 8000, 5000, 9000, 6000]
}
df = pd.DataFrame(data)
df = df.replace(['路人甲'],['李四'])
print(df)
name age salary
0 Alice 25 5000
1 Bob 30 7000
2 Charlie 35 8000
3 Alice 25 5000
4 David 40 9000
5 李四 25 6000
也可以使用字典形式。
df = df.replace({'路人甲':'李四'})
如果要多值替换,使用逗号分隔就行。
说白了就是自定义数据转换规则,使用map方法进行映射就行,不再赘述。
数据都得有个标准,或者说是量纲,为了消除量纲差异之间的影响,应该实现数据标准化。
使用这么一个公式:
x1 = (x - min) / (max -min)
写成函数就是:
df['age_minmax'] = (df['age'] - age_min) / (age_max - age_min)
使用这么一个公式:
x1 = (x - mean) / std
写成函数就是:
df['age_zscore'] = (df['age'] - age_mean) / age_std
mean是平均值,std是标准差
肯定会有人问:这样做岂不是改变了原数据的值吗?那不会影响到数据精确性吗?
数据标准化确实会改变数据的原始值,但其目的是为了更好地进行数据分析和建模。标准化不会改变数据的内在信息和结构,而是通过缩放或转换数据使其更适合特定的分析方法。
数据分析的预处理除了数据清洗、数据合并和标准化之外,还有类别型数据变换和连续性数据的离散化。
Python不能直接处理非数值型的变量,需要对这些变量进行转换。
哑变量(又称为虚拟变量),是用来反映质的属性的一个人工变量。通常取值True或者False。
我们先来看一个例子来更好理解。
# 类别型数据的转换
df = pd.DataFrame([
['green','M',120],
['blue','XL',150],
['red','XXL',200]
])
df.columns = ['color','size','prize']
print(df)
df1 = pd.get_dummies(df)
print(df1)
color size prize
0 green M 120
1 blue XL 150
2 red XXL 200
prize color_blue color_green color_red size_M size_XL size_XXL
0 120 False True False True False False
1 150 True False False False True False
2 200 False False True False False True
可以看到,哑变量的使用使得那些不是数值类型的变量被拆分开来,通过把它们的数值名变成根据实际数据来进行分类,通过“是或者否”来进行赋值。这样很巧妙,把原先不是数值的类型,转变成了布尔值,从而更好地进行数据处理。
如果取值有m个,那么经过哑变量处理后就变成了2^m个特征,毕竟是只有True或者False两种情况。
使用cut
方法,参数如下:
参数 | 说明 |
---|---|
x | 待离散化的数据 |
bins | 表示离散化后的区间 后者是类别数目 |
right | 代表右侧是否为闭区间,默认是True |
labels | 表示离散化后各个类别的名称 |
retbins | 代表是否返回区间标签,默认不返回 |
precision | 显示标签的精度,默认为3 |
等宽法可能会有类分布不均匀的问题,我们修改cut
的用法就能实现等频。指定一个方法(0,1+0,1/k,1.0/k)
都说物以类聚,我们可以使用聚类分析法来指定聚类的标准,为合并到同一个簇的连续性数据做同一个标记,进行数据的区分。