Python读取数据、数据操作

从数据源读取数据的方法

常见的数据源

csv,xlsx, txt, db

从Excel中读取数据

使用

1
pd.read_excel(r'文件路径') # 在jupyter中将光标放到括号里面,使用shift+tab来获取函数的帮助信息

来读取指定位置xlsx文件的数据

如何方便的找到其他目录下的某个文件

1
2
3
4
5
6
7
8
import os

print(os.getcwd()) # 此时是该代码所在文件所在的路径

os.chdir(r'新工作区路径') # 改变当前默认工作路径
print(os.getcwd()) # 此时工作路径改变
#此时再使用pd.read_excel()就行了
df1 = pd.read_excel(r'文件名.xlsx')

如何读取Excel文件中指定sheet的数据

1
2
df2 = pd.read_excel(r'路径.xlsx', 
sheet_name = 'Sheet2') # 读取sheet2上面的数据

针对没有数据标题的数据表,必须指定header = None

1
2
3
df2 = pd.read_excel(r'路径.xlsx', 
sheet_name = 'Sheet2',
header = None) # 读取sheet2上面的数据,并且不让该sheet的第一行作为表头(列标签)

指定第一列作为行标签的方法

1
2
3
4
df2 = pd.read_excel(r'路径.xlsx', 
sheet_name = 'Sheet2',
header = None,
index_col = 0) # 读取sheet2上面的数据,并且不让该sheet的第一行作为表头(列标签),并且让该sheet的第一列作为行标签

修改默认列标签为自定义列标签

1
2
3
4
df2 = pd.read_excel(r'路径.xlsx', 
sheet_name = 'Sheet2',
header = None, index_col = 0,
names = ['列' + str(i) for i in range(1,6)]) # 读取sheet2上面的数据,并且不让该sheet的第一行作为表头(列标签),并且让该sheet的第一列作为行标签,并将列标签改为列1-列6

指定只读取多少行

1
2
3
4
5
df2 = pd.read_excel(r'路径.xlsx', 
sheet_name = 'Sheet2',
header = None, index_col = 0,
names = ['列' + str(i) for i in range(1,6)],
nrows = 3) # 读取sheet2上面的数据,并且不让该sheet的第一行作为表头(列标签),并且让该sheet的第一列作为行标签,并将列标签改为列1-列6,并且只读取前三行

只导入指定的列

1
2
3
4
5
6
df2 = pd.read_excel(r'路径.xlsx', 
sheet_name = 'Sheet2',
header = None, index_col = 0,
names = ['列' + str(i) for i in range(1,6)],
nrows = 3,
usecols = ['列1', '列2', '列3']) # 读取sheet2上面的数据,并且不让该sheet的第一行作为表头(列标签),并且让该sheet的第一列作为行标签。并将列标签改为列1-列6。并且只读取前三行。并且只读取前三列(可以使用列名也可以使用列的index)

指定省略行

1
2
3
df2 = pd.read_excel(r'路径.xlsx', 
sheet_name = 'Sheet3'
skiprows = [0]) # 读取sheet2上面的数据,省略掉第0行

从CSV中读取数据

使用

1
df = pd.read_csv(r'文件路径') # 读取csv文件

读取含有中文的csv文件

1
df = pd.read_csv(r'文件路径_文件内含中文', engine = 'python') # 读取csv文件

如果是非逗号分割的csv文件的读取

1
df = pd.read_csv(r'文件路径_#作为分隔符', sep = '#') # 读取csv文件, csv以#分割列

读取文本文件(txt)

使用

1
df = pd.read_table(r'文件路径_含中文', engine = 'python') # 读取txt文件

读取含中文的文件,如果不含中文,则不需要指定engine参数

处理特殊分列符

1
df = pd.read_table(r'文件路径_含中文', engine = 'python', sep = '@') # 读取txt文件, 列之间以@分割

数据预处理阶段(数据清洗)

数据预处理一般包括以下五个方面:

  1. 缺失值处理
  2. 重复值处理
  3. 异常值处理
  4. 数据类型转换
  5. 索引设置

缺失值处理

如果数据集比较大,缺失值占比较小的话可以直接删除,如果数据集较小,则不能直接删除。常见的处理方法

  1. 填充一个平均值(年收入)
  2. 填充一个众数(上下班打卡)
  3. 最小值(商品价格)
  4. 平均值
  5. 最大值(保险收益)

第一步:查看缺失值(不直接查看源文件)

1
2
3
4
5
df = pd.read_excel(r'filename.xlsx')

#思路:先看数据集中各列有没有缺失值,再看缺失值所占比例,根据缺失值比例进行处理
df.isnull().sum() # 查看数据缺失值的情况
df.apply((lambda x: sum(x.isnull()) / len(x)), axis = 0) # 查看每一列缺失值所占的比例,axis参数表示按照列进行运算

第二步(方法1):删除缺失值

1
df.dropna() # 删除所有含有NaN的行(创建副本的方式,非原地修改),可以使用inplace = True参数

可以指定axis参数,如果axis = 1,则会删除含有NaN的列,如下:

1
df.dropna(axis = 1) # 删除所有含有NaN的列(创建副本的方式,非原地修改),可以使用inplace = True参数

指定当某行所有值全为NaN时才删除,如下:

1
df.dropna(how = 'all') # how 参数默认为any,即有任何NaN则删。

第二步(方法2):填充缺失值(缺失值使用文字填充,如‘暂无’)

首先是替换(如果需要)

1
2
3
df2 = pd.read_excel(r'filename.xlsx', na_values = '暂无') # 将‘暂无’替换成NaN(读取时直接将‘暂无’替换成缺失值)
# 如果在读取的时候没有指定na_values参数,则可以使用
df.replace('暂无', np.nan) # 将‘暂无’修改为缺失值,非原地修改(创建副本的方式,需要通过指定inplace = True来覆盖原DataFrame)

注意:Pandas中只要涉及DataFrame修改的操作全部都是创建副本的形式,也就是非原地修改,需要指定inplace = True,目的是防止用户误操作数据

其次是替换插值(使用平均值)

1
2
3
4
5
6
7
df['售价'] = df['售价'].apply(lambda x : str(x).replace('万', '') if x is not np.nan else x) # 将数值后面的单位去掉

df['售价'] = df['售价'].astype(np.float64) # 强制类型转换
df['售价'].mean() # 直接求某列平均值

df.fillna({'售价' : df['售价'].mean()}) # 填充缺失值(该列所有缺失值全部为该列平均值)
# 以此类推,还有df.max(), df.min()

重复值处理

重复值一般作为删除处理

1
2
3
4
5
6
7
8
9
df = pd.read_excel(r'重复值处理.xlsx') # 读取数据

df.drop_duplicates() # 调用方法直接删除重复值,该方法默认删除的是整行所有值全部重复的行(非原地修改,inplace = True指定之后即可原地修改)。删除之后index不连续。该方法保留的是所有重复值的第一条
df.reset_index(drop = True/False) # 将不连续的索引变成连续的索引,但是直接调用会在前面新加一列(保留原来不连续索引)。只需指定drop = True即可彻底删除不连续的index

df.drop_duplicates(keep = 'last') # 保留重复值的最后一条
df.drop_duplicates(keep = False) # 只要有重复,全部删除,一条不留。

df.drop_duplicates(subset = ['日期', '售价']) # 根据某列/多列重复作为判断重复的依据

异常值处理

异常值就是相比正常数据而言过高或过低的数据

异常值的特征

  1. 异常值并不是错误值,他和正常范围的数据偏离较大
  2. 异常值出现的频率低,但又会对实际的项目分析造成偏差
  3. 异常值一般采取盖帽法或数据离散化进行处理

异常值的检测

  1. 根据业务经验来判断(粗筛,数据量小)
  2. 根据数据的分布形态,如果数据服从正态分布,就可以利用3σ原则:如果一个数值与平均值之间的偏差超过3倍标准差,那么就可以认为这个值是一个异常值
  3. 对于不服从正态分布的数据,通过绘制箱型图,把大于/小于箱型图上边缘/下边缘的点称为异常值(对数据没有特别要求)

对于符合正态分布的异常点的检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 首先通过画图来判断是否符合正态分布
df = pd.read_csv(r'正态分布.csv', index_col = 0)

ax = df['weight_data'].plot(kind = 'hist') # kind参数指定了plot应该画一个什么样的图(hist代表直方图)
df['weight_data'].plot(kind = 'kde', ax = ax, secondary_y = True) # kde是和密度图

import matplotlib.pyplot as plt
fig = plt.figure(figsize = (10, 5))
plt.subplot2grid((1, 2), (0, 0)) # 设置子图,一行两列,当前是(0,0)
plt.plot(df.index,
df['weight_data'],
linestyle = '-',
linewidth = 2,
color = 'steelblue',
marker = 'o',
makersize = 2,
markeredgecolor = 'black'
markertracecolor = 'black') # 对图像进行一些设置

plt.axhline(y = df['weight_data'].mean() - 2 * df['weight_data'].std(), linestyle = '--', color = 'red') # -2σ处画一道横线
plt.axhline(y = df['weight_data'].mean() + 2 * df['weight_data'].std(), linestyle = '--', color = 'red') # +2σ处画一道横线

plt.subplot2grid((1, 2), (0, 0)) # 设置子图,一行两列,当前是(0,1)
plt.plot(df.index,
df['weight_data'],
linestyle = '-',
linewidth = 2,
color = 'steelblue',
marker = 'o',
makersize = 2,
markeredgecolor = 'black'
markertracecolor = 'black') # 对图像进行一些设置

plt.axhline(y = df['weight_data'].mean() - 3 * df['weight_data'].std(), linestyle = '--', color = 'red') # -3σ处画一道横线
plt.axhline(y = df['weight_data'].mean() + 3 * df['weight_data'].std(), linestyle = '--', color = 'red') # +3σ处画一道横线

#异常点
lower_ = df['weight_data'].mean() - 2 * df['weight_data'].std()
upper_ = df['weight_data'].mean() + 2 * df['weight_data'].std()

#极端异常点
extreme_lower = df['weight_data'].mean() - 3 * df['weight_data'].std()
extreme_upper = df['weight_data'].mean() + 3 * df['weight_data'].std()

#选择异常点
df.loc[((df['weight_data']) > upper_) | ((df['weight_data']) < lower_)]
#选择极端异常点
df.loc[((df['weight_data']) > extreme_upper) | ((df['weight_data']) < extreme_lower)]

根据箱型图的规律判断异常点

1
2
3
4
5
df = pd.read_csv(r'non-normal.csv', index_col = 0)

#先画图
ax = df['Count'].plot(kind = 'hist')
df['Count'].plot(kind = 'kde', ax = ax, secondary_y = True)

在箱型图中,上四分位数为Q3,下四分位数记录为Q1,则上界的计算公式为:Q3 + 1.5 \ (Q3 - Q1),下界的计算公式:Q1 - 1.5 * (Q3 - Q1)。其中(Q3 - Q1)表示四分位差*

1
2
3
4
5
6
7
Q1 = df.Count.quantile(q = 0.25) # 求分位数函数
Q3 = df['Count'].quantile(q = 0.75)

upper_bound = Q3 + 1.5 * (Q3 - Q1)
lower_bound = Q1 - 1.5 * (Q3 - Q1)

df['Count'][(df['Count'] > upper_bound) | (df['Count'] < lower_bound)]

数据类型转换

一般打开文件获取到的都是object类型的数据,所以需要数据类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
df = pd.read_excel(r'数据类型转换.xlsx')

#利用astype方法可以对数值型数据进行类型转换
df['售价'].astype(np.int32) # 原数据为浮点型,转换为整型
df['身高'].astype(np.float64)

# 对日期格式进行转换
df['日期'] = pd.to_datatime(df['日期'], format = '%Y-%m-%d')
df = pd.read_excel(r'数据类型转换.xlsx', prase_dates = ['日期']) # 在读取数据的时候进行日期的格式转换

#直接使用字符串方法
df['部门'].str.replace('部', '组') # 转成字符串之后可以替换
df['部门'].str[:2] # 也可以使用字符串切片

索引设置

主要分为添加索引和设置索引。当数据集中缺少行索引、列索引时,可以添加索引

1
2
3
4
5
6
df = pd.read_excel(r'索引设置.xlsx', header = None)
#将行和列的索引进行设置
df.columns = ['加入日期', '销售额', '等级', '姓名', '部门', '身高']
df.inedx = ['a', 'b', 'c', 'd', 'e', 'f']

df.set_index('加入日期', implace = True) # 将‘加入日期’这一列设为索引列,好处是可以直接访问

数据选择

列选择

1
2
3
4
5
6
7
8
9
10
11
12
df = pd.read_excel(r'数据类型转换.xlsx')

# 选择一列
df['售价']
#选择多列
df['售价', '类型']

# 通过列的位置索引来选择列 df.iloc()方法,该方法接受两个参数,第一个是行的坐标,第二个是列的坐标
# 坐标可以是两种方式:一种是像列表一样切片的语法,用于选择连续的行或列;另外一种是具体的行或列的坐标,用于选择具体的某几行或某几列。

df.iloc[:, 1 : 5] # 类似字符串截取的方式来选择行和列(所有行和1到4列),显示行同理
df.iloc[[0, 2, 4], [0, 2, 3]] # 用来选择不连续的行和列

注意:如果不指定列,那么所有的列都会参与,如下

1
df.iloc[[0, 2, 4]] # 不指定列参数就是所有列

通过标签来选择行

1
2
df.loc[2] # 通过标签来选择,方括号内填写的是行标签
df.loc[2 : 5] # 也可以通过切片的方法来选取行

注意:如果更改了行标签,则不能用数字进行访问,只能用修改后的标签来访问,如下

1
2
3
4
5
6
df.set_index('日期', inplace = True)

df['2020/03/05'] # 新的行标签
df.[['2020/03/05', '2020/02/02']] # 选择多个行标签

df.loc[['2020/03/05', '2020/02/02'], ['销售额', '操作人']] # 交叉选择

注意:只有iloc才可以通过index访问,loc只能通过标签值来访问

按条件来选择行

1
2
3
df[df['售价'] > 30] # 将‘售价’这一列中所有大于30的行选择出来
df[(df['售价'] > 30) & (df['身高'] < 170)] # 多个条件筛选(同时满足)
df[(df['售价'] > 30) | (df['身高'] < 170)] # 多个条件筛选(任一满足)

交叉索引 df.ix

1
df.ix[[0:3], ['部门', '身高']] # 即将被遗弃的方法