一个数据分析师,最怕的一件事情莫过于在没有数据的情况下,让你去做一个详细的数据分析报告。确实,巧妇难为无米之炊,数据是数据分析、数据挖掘乃至数据可视化最最基础的元素。
利用Python进行数据分析最重要到一步,就是利用合适的方法将数据导入到Python。然而,当你面对一堆数据,你真的会快速、正确的读取吗?
在本期Python数据分析实战学习中,将从常见的数据获取方法入手,对常用的数据获取方式进行详细的介绍:
第一招 Open( )函数读取数据
Python内置函数open( ),主要用来从文本中读取数据。
Python可以读取任何格式的文本数据。一般分为三个步骤:定义数据文件、创建文件对象、读取文件内容。
语法
将文件赋值给一个文件对象,为了后续操作更加便捷,减少代码冗余。
file_name1 = './test.txt'
file_name2 = '/Users/jim/Documents/Python/test.txt'
file_name1:为相对路径,其要求需脚本路径与文件路径一致。 file_name2:为绝对路径,无其他要求。
1、语法
要以读文件的模式打开一个文件对象,使用Python内置的open( )函数,传入文件名和标示符,其意义在于后续的操作均是基于该对象产生的。
file_object = open(name [, mode][, buffering])
name: 要读取的文件名称。 mode: 打开文件的模式,选填。
r, r+, w, w+, a, a+
使用最多。 buffering: 文件所需的缓冲区大小, 选填。0表示无缓冲, 1表示线路缓冲。
Mode | Describe |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 |
w | 打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。 |
rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。 |
r+ | 打开一个文件用于读写。文件指针将会放在文件的开头。 |
w+ | 打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 |
wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |
例
>>> file_object = open(file_name, 'r')
# 文件不存在,即报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: './test.txt'
>>> file_object.read()
'Hello world!'
2、Python基于文件对象分为3种方法
hon基于文件对象分为3种方法
Methods | Describe | Return |
---|---|---|
read | 读取文件中的全部数据,直到到达定义的size字节数上限 | 内容字符串,所有行合并为一个字符串 |
readline | 读取文件中的一行数据,直到到达定义的size字节数上限 | 内容字符串 |
readlines | 读取文件中的全部数据,直到到达定义的size字节数上限 | 内容列表,每行数据作为列表中的一个对象 |
例
# test.txt中有两行内容:
"""
line1: Hello world!
line2: Life is short. I learn Python!
"""
>>> file_object = open(file_name)
>>> read_data = file_object.read()
>>> print(read_data)
line1: Hello world!
line2: Life is short. I learn Python!
>>> readline_data = file_object.readline()
>>> print(readline_data)
line1: Hello world!
>>> readlines_data = file_object.readlines()
>>> print(readlines_data)
line1: Hello world!
line2: Life is short. I learn Python!
遇到有些编码不规范的文件,你可能会遇到UnicodeDecodeError
,因为在文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open( )
函数还接收一个errors
参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略:
file_object = open('/Users/jim/Documents/Python/gbk.txt', 'r', encoding='gbk', errors='ignore' )
readline
每次只读取一行数据,需配合seek
, next
等指针操作,才能完整遍历所有数据记录。
>>> fout = open('text.txt') # 获得文件对象
>>> print(fout.tell()) # 输出指针位置
0
>>> line1 = fout.readline() # 获得文件第一行数据
>>> print(line1) # 输出第一行数据
line1: Hello world!
>>> print(fin.tell()) # 输出指针位置
21
>>> line2 = fout.readline() # 获得文件第二行数据
>>> print(line2) # 输出第二行数据
line2: Life is short. I learn Python!
>>> print(fout.tell()) # 输出指针位置
>>> fout.close() # 关闭文件对象
60
由于文件读写时都有可能产生IOError
,一旦出错,后面的fout.close()
就不会调用。可以使用try … finally
来保证无论是否出错都能正确地关闭文件:
>>> try:
... file_object = open('./text.txt', 'r')
... print(file_object.read())
... finally:
... if file_object:
... file_object.close()
3、基于with的文件打开方法
相信很多时候,在使用open( )
函数时,总不是很方便。此时使用基于with
的文件打开方法,可以自动做上下文管理,而无需单独做close
操作,简单又方便:
例1 对单个文件对象操作时:
>>> with open( './test.txt', 'r' ) as fout:
... print(fout.read())
line1: Hello world!
line2: Life is short. I learn Python!
例2 同时对多个文件对象操作,可以连续写open方法:
>>> with open( './test1.txt', 'r' ) as fout1, open( './test2.txt', 'r' ) as fout2:
... content1 = fout1.read()
... content2 = fout2.read()
调用
read()
会一次性读取文件的全部内容,如果文件有10G,内存就爆了。可以反复调用read(size)
方法,每次最多读取size
个字节的内容。 调用readline()
可以每次读取一行内容,调用readlines()
一次读取所有内容并按行返回list
。 如果文件很小,read()
一次性读取最方便;如果不能确定文件大小,反复调用read(size)
比较保险;如果是配置文件,调用readlines()
最方便。
第二招 Pandas 库读取数据
在日常数据分析中,使用pandas读取数据文件更为常见。pandas不仅可以读取open()函数所读取的文本文件及其他各类文件,最重要的是pandas读取结果为DataFrame数据框,后续的数据处理更为方便。
1、语法
以最常用的读取csv
文本文件数据为例,对pandas读取数据进行详细对介绍。
>>> import pandas as pd
>>> df = pd.read_csv(r"./test.csv" # 路径里面可以是中文,到时如果有特殊字符,可能会报错,建议路径全是英文。
, sep = ',' # 默认分隔符为,
, header = 'infer' # 默认将第一行作为列名 ,header = None不要一第一行作为标题。
, encoding='gbk' # 默认用 UTF-8 进行解码,读取window系统建立的csv文件需改成`encoding='gbk'`
, index_col=None
)
>>> df.head(4) # df.head()默认显示前5行, 当然可以自己制定sh
输出结果:
常用参数说明:
sep : str, default ‘,’ 指定分隔符。如果不指定参数,则会尝试使用逗号分隔。分隔符长于一个字符并且不是‘\s+’,将使用python的语法分析器。并且忽略数据中的逗号。正则表达式例子:'\r\t' header : int or list of ints, default ‘infer’ 指定行数用来作为列名,数据开始行数。如果文件中没有列名,则默认为0,否则设置为None。如果明确设定
header=0
就会替换掉原来存在列名。header
参数可以是一个list例如:[0,1,3],这个list表示将文件中的这些行作为列标题(意味着每一列有多个标题),介于中间的行将被忽略掉(例如本例中的2;本例中的数据1,2,4行将被作为多级标题出现,第3行数据将被丢弃,DataFrame的数据从第5行开始。)。注意:如果skip_blank_lines=True 那么header参数忽略注释行和空行,所以header=0表示第一行数据而不是文件的第一行。 index_col : int or sequence or False, default None 用作行索引的列编号或者列名,如果给定一个序列则有多个行索引。 如果文件不规则,行尾有分隔符,则可以设定index_col=False
来是的pandas不适用第一列作为行索引。 encoding : str, default None 指定字符集类型,通常指定为'utf-8'. dtype : Type name or dict of column -> type, default None 每列数据的数据类型。例如 {‘a’: np.float64, ‘b’: np.int32} nrows : int, default None 需要读取的行数(从文件头开始算起) skiprows : list-like or integer, default None 需要忽略的行数(从文件开始处算起),或需要跳过的行号列表(从0开始)。 low_memory : boolean, default True 分块加载到内存,再低内存消耗中解析。但是可能出现类型混淆。确保类型不被混淆需要设置为False。或者使用dtype 参数指定类型。注意使用chunksize 或者iterator 参数分块读入会将整个文件读入到一个DataFrame,而忽略类型(只能在C解析器中有效) parse_dates : boolean or list of ints or names or list of lists or dict, default False
2、常见问题
csv
>>> import pandas as pd
>>> #df=pd.read_csv("E:/测试文件夹/测试数据.csv")
>>> f=open("E:/测试文件夹/测试数据.csv") # 解决方案
>>> df=pd.read_csv(f)
shift+右键-->复制为路径
获取的文件路径
>>> import pandas as pd
>>> # df=pd.read_csv("E:\测试文件夹\测试数据.csv")
>>> df=pd.read_csv(r"E:\测试文件夹\测试数据.csv")
字符串前加 r
的作用
>>> "E:\测试文件夹\测试数据.csv"
'E:\\测试文件夹\\测试数据.csv'
>>> r"E:\测试文件夹\测试数据.csv"
'E:\\测试文件夹\\测试数据.csv'
>>> print("E:\测试文件夹\test.csv")
E:\测试文件夹 est.csv
>>> print(r"E:\测试文件夹\test.csv")
E:\测试文件夹\test.csv
>>> import pandas as pd
>>> df = pd.read_csv(r"./test.csv"
... , skiprows=3 # 要注意的是:排除前3行是skiprows=3 排除第3行是skiprows=[3]
... , nrows=2
... , encoding='gbk')
>>> df
输出结果:
>>> import pandas as pd
>>> df = pd.read_csv(r"./test.csv", encoding='gbk'
... #, parse_dates=[3]
... )
>>> df.loc[0,'就诊日期']
2018/6/15
>>> df = pd.read_csv(r"./test.csv", encoding='gbk'
... , parse_dates=[3]
... )
>>> df.loc[0,'就诊日期']
Timestamp('2018-06-15 00:00:00')
避坑指南:
有日期时间格式列的文件作为缓存文件,先用
test.to_csv('test.csv')
保存,再用pd.read_csv('./test.csv')
读取文件时。 坑1:index
列。保存文件时默认保存索引,读取文件时默认自动添加索引列,即将保存的索引作为第一列读取到DataFrame
。 解决方案: 1,test.to_csv('test.csv', index=False)
2,pd.read_csv('./test.csv', index_col=0)
坑2:原本日期格式的列,保存到csv
文件后仍为日期格式。但再次读取文件时将以字符串的格式读取到DataFrame
。
解决方案:
1, pd.read_csv('./test.csv', parse_dates=[3])
将特定的日期列解析为日期格式;
2, 先使用默认值file = pd.read_csv('./test.csv')
,再对特定的列进行格式转换。file.loc[:, column] = file.loc[:, column].map(lambda x: parse(x).date() if isinstance(file.loc[0, column], str) else x)
更多详情,请见 pandas 官方文档查阅地址:
https://pandas.pydata.org/pandas-docs/version/0.24/reference/io.html
第三招 Numpy 库读取数据
Numpy读取数据方法与Pandas类似,其包括loadtxt, load, fromfile
Methods | Describe | Return |
---|---|---|
loadtxt | 从txt文本中读取数据 | 从文件中读取的数组 |
load | 使用numpy的load方法可以读取numpy专用的二进制数据文件,从npy, npz或pickled文件中加载数组或pickled对象 | 从数据文件中读取的数据、元祖、字典等 |
fromfile | 使用numpy的fromfile方法可以读取简单的文本文件数据以及二进制数据 | 从文件中读取的数据 |
数据通常是一维或者二维的
语法
np.loadtxt( fname
, dtype=<class 'float'>
, comments='#'
, delimiter=None
, converters=None
, skiprows=0
, usecols=None
, unpack=False
, ndmin=0
, encoding='bytes'
, max_rows=None
,)
常用参数说明:
fname : file, str, or pathlib.Path 文件或字符串, 必填项, 指要读取的文件名称或字符串, 支持压缩的数据文件, 包括
gz
和bz
格式。 dtype : data-type, optional 数据类型, 选填, 默认为float。 comments : str or sequence of str, optional 字符串或字符串组成的列表, 选填,默认 #, 是表示注释字符集开始的标志。 delimiter : str, optional 字符串, 选填, 默认空格, 用来分隔多个列的分隔符, 如逗号、TAB符。 converters : dict, optional 字典, 选填, 默认为空, 用来将特定列的数据转换为字典中对应的函数的浮点型数据。如果第0列是一个date则'converters = {0: datestr2num} '; 'converters = {3: lambda s: float(s.strip() or 0)}'
skiprows : int, optional 跳过特定行数据, 选填, 默认为0, 用来跳过特定前N条记录。 usecols : int or sequence, optional 整数或元祖, 选填, 默认为空, 用来指定要读取数据的列, 如(1, 3, 6) unpack : bool, optional 布尔值, 选填, 默认为False, 用来指定是否转置, 如果为True, 则转置 ndmin : int, optional 整数型, 选填, 默认为0, 用来指定返回的数据至少包含特定维度的数组, 值域为0/1/2 encoding : str, optional 字符串, 选填, 用于解码inputfile的编码。不适用于输入流。特殊值"bytes"
允许向后兼容解决方案, 这可以确保接收到字节数组作为结果, 如果可能的话“latin1”编码的字符串到转换器。重写此值以接收unicode
数组, 并将字符串作为输入传递给转换器。如果没有设置, 使用系统默认值。默认值是"bytes"
。 max_rows : int, optional 整数, 选填, 默认为空, 在"skiprows"
行之后读取内容的"max_rows"
行。默认的就是读所有的行。
例
>>> import numpy as np # 导入numpy库
>>> file_name = 'numpy_data.txt' # 定义数据文件
>>> data = np.loadtxt(file_name, dtype='float32', delimiter=' ') # 获取数据
>>> print(data) # 打印数据
[[ 0. 1. 2. 3. 4.]
[ 5. 6. 7. 8. 9.]
[10. 11. 12. 13. 14.]]
使用numpy
的load
方法可以读取numpy
专用的二进制数据文件,从npy
, npz
或pickled
文件中加载数组或pickled
对象, 该文件通常基于numpy
的save
或savez
产生。
语法
np.load(file
, mmap_mode=None
, allow_pickle=False
, fix_imports=True
, encoding='ASCII')
file : file-like object, string, or pathlib.Path 类文件对象或字符串格式, 必填, 要读取的文或字符串。类文件对象需要支持
seek()
和read()
方法。 mmap_mode : {None, 'r+', 'r', 'w+', 'c'}, optional 内存映射模式, 选填。 allow_pickle : bool, optional 布尔值, 选填, 默认为True
, 决定是否允许加载存储在npy
文件中的pickled
对象数组。 fix_imports : bool, optional 布尔值, 选填, 默认为True
, 只有在python3
上加载python2
生成的pickle
文件时才有用, 其中包括包含对象数组的npy/npz
文件。如果"fix_imports"
, 如果是True
,pickle
将尝试将旧的python2
名称映射到新名称在python3
中使用。 encoding : str, optional 在读取Python 2
字符串时使用什么编码。加载python2
生成了python3
中的pickle
文件时才有用, 其中包括包含对象数组的npy/npz
文件。除了latin1
,"ASCII"
和"bytes"
是不允许的, 因为它们会破坏数字数据。默认值:"ASCII"
例
>>> import numpy as np # 导入numpy库
>>> write_data = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) # 定义要存储的数据
>>> np.save('load_data', write_data) # 保存为npy数据文件
>>> read_data = np.load('load_data.npy') # 读取npy文件
>>> print(read_data) # 输出读取的数据
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
该方法读取的数据来源于numpy的tofile方法。
语法
fromfile(file, dtype=float, count=-1, sep='', offset=0)
file : file or str or Path 文件或字符串或路径 dtype : data-type, optional 数据类型, 选填, 默认为float。 count : int 整数型, 读取数据的数量, -1意味着读取所有的数据。 sep : str 字符串, 如果文件是文本文件, 那么该值为数据间的分隔符。空("")分隔符表示该文件应该作为二进制文件处理。分隔符中的空格(" ")匹配零个或多个空格字符。仅由空格组成的分隔符必须至少匹配一个空白。
例
>>> import numpy as np # 导入numpy库
>>> file_name = 'numpy_data.txt' # 定义数据文件
>>> data = np.loadtxt(file_name, dtype='float32', delimiter=' ') # 获取数据
>>> tofile_name = 'binary' # 定义导出二进制文件名
>>> data.tofile(tofile_name) # 导出二进制文件
>>> fromfile_data = np.fromfile(tofile_name, dtype='float32') # 读取二进制文件
>>> print(fromfile_data) # 打印数据
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.]
另外,使用Python读取Excel文件,除了使用pandas.read_excel()
,还是采用专门用于读取Excel的第三方库,最常用的是xlrd。