Python Pandas——Read_csv详解
目前最常用的数据保存格式可能就是CSV格式了,数据分析第一步就是获取数据,怎样读取数据至关重要。
本文将以pandas read_csv方法为例,详细介绍read_csv数据读取方法。再数据读取时进行数据预处理,这样不仅可以加快读取速度,同时为后期数据清洗及分析打下基础。
导入必要的库
import pandas as pd
import numpy as np
from pandas.api.types import CategoricalDtype
from io import StringIO
Specifying Column Data Types
可以指定整个DataFrame或各个列的数据类型:
data = pd.read_csv('diamonds.csv',dtype=object)
data.head()
out:
carat cut color clarity depth table price x y z
0 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43
1 0.21 Premium E SI1 59.8 61 326 3.89 3.84 2.31
2 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31
3 0.29 Premium I VS2 62.4 58 334 4.2 4.23 2.63
4 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75
data.dtypes
out:
carat object
cut object
color object
clarity object
depth object
table object
price object
x object
y object
z object
dtype: object
data = pd.read_csv('diamonds.csv',dtype={'carat': np.float64,'depth': np.float64,'table':np.float64})
data.dtypes
out:
carat float64
cut object
color object
clarity object
depth float64
table float64
price int64
x float64
y float64
z float64
dtype: object
pandas提供了多种方法来确保列仅包含一个dtype。例如,可以使用read_csv()的converters参数:
data = pd.read_csv('diamonds.csv',converters={'carat':str})
data.dtypes
out:
carat object
cut object
color object
clarity object
depth float64
table float64
price int64
x float64
y float64
z float64
dtype: object
data.carat.apply(type).value_counts()
out:
53940
Name: carat, dtype: int64
或者,可以在读取数据后使用to_numeric()函数强进行类型转换。
data.carat = pd.to_numeric(data['carat'],errors='coerce')
data.carat.apply(type).value_counts()
out:
53940
Name: carat, dtype: int64
Specigying Categorical Dtype¶
可以通过指定dtype ='category'或dtype = CategoricalDtype(类别,有序)直接解析类别列。
data = pd.read_csv('diamonds.csv',dtype='category')
data.dtypes
out:
carat category
cut category
color category
clarity category
depth category
table category
price category
x category
y category
z category
dtype: object
可以使用dict指定将某列为Category类型:
data = pd.read_csv('diamonds.csv',dtype={'cut':'category'})
data.dtypes
out:
carat float64
cut category
color object
clarity object
depth float64
table float64
price int64
x float64
y float64
z float64
dtype: object
data.cut.value_counts()
out:
Ideal 21551
Premium 13791
Very Good 12082
Good 4906
Fair 1610
Name: cut, dtype: int64
指定dtype ='category'将导致无序分类,其类别是数据中观察到的唯一值。
要更好地控制类别和顺序,可以创建CategoricalDtype,然后将其传递给该列的dtype。
from pandas.api.types import CategoricalDtype
dtype = CategoricalDtype(['Ideal','Premium','Very Good','Good','Fair'],ordered=True)
data = pd.read_csv('diamonds.csv',dtype={'cut':dtype})
data.dtypes
out:
carat float64
cut category
color object
clarity object
depth float64
table float64
price int64
x float64
y float64
z float64
dtype: object
使用dtype = CategoricalDtype时,dtype.categories之外的“意外”值将被视为缺失值。
from pandas.api.types import CategoricalDtype
dtype = CategoricalDtype(['Ideal','Premium','Very Good','Good'],ordered=True)
data = pd.read_csv('diamonds.csv',dtype={'cut':dtype})
data[data.cut.isnull()].head()
out:
carat cut color clarity depth table price x y z
8 0.22 NaN E VS2 65.1 61.0 337 3.87 3.78 2.49
91 0.86 NaN E SI2 55.1 69.0 2757 6.45 6.33 3.52
97 0.96 NaN F SI2 66.3 62.0 2759 6.27 5.95 4.07
123 0.70 NaN F VS2 64.5 57.0 2762 5.57 5.53 3.58
124 0.70 NaN F VS2 65.3 55.0 2762 5.63 5.58 3.66
Naming and Using Columns
Handling Column names
文件可能包含标题行,也可能没有标题行。 pandas假定第一行应用作列名:
from io import StringIO
data = ('a,b,c\n'
'1,2,3\n'
'4,5,6\n'
'7,8,9')
pd.read_csv(StringIO(data))
out:
a b c
0 1 2 3
1 4 5 6
2 7 8 9
通过指定name与header,可以重命名列以及是否丢弃标题行:
pd.read_csv(StringIO(data),names=['foo','bar','baz'],header=0)
out:
foo bar baz
0 1 2 3
1 4 5 6
2 7 8 9
pd.read_csv(StringIO(data),names=['foo','bar','baz'],header=None)
out:
foo bar baz
0 a b c
1 1 2 3
2 4 5 6
3 7 8 9
如果标题不在第一行中,则将行号传递给标题,将跳过header前面的行:
data = ('skip this skip it\n'
'a,b,c\n'
'1,2,3\n'
'4,5,6\n'
'7,8,9')
pd.read_csv(StringIO(data),header=1)
out:
a b c
0 1 2 3
1 4 5 6
2 7 8 9
Duplicate Names Parsing
如果文件或标题包含重复的名称,默认情况下,pandas会将它们区分开,以防止覆盖数据.
data = ('a,b,a\n'
'0,1,2\n'
'3,4,5')
print(data)
out:
a,b,a
0,1,2
3,4,5
pd.read_csv(StringIO(data))
out:
a b a.1
0 0 1 2
1 3 4 5
Filtering Columns(usecols)
usecols参数允许您使用列名,位置号或可调用的方法选择文件中列的任何子集.
data = 'a,b,c,d\n1,2,3,foo\n4,5,6,bar\n7,8,9,baz'
pd.read_csv(StringIO(data))
out:
a b c d
0 1 2 3 foo
1 4 5 6 bar
2 7 8 9 baz
pd.read_csv(StringIO(data),usecols=['b','d'])
out:
b d
0 2 foo
1 5 bar
2 8 baz
pd.read_csv(StringIO(data),usecols=[0,1,3])
out:
a b d
0 1 2 foo
1 4 5 bar
2 7 8 baz
pd.read_csv(StringIO(data),usecols=lambda x: x.upper() in ['A','C'])
out:
a c
0 1 3
1 4 6
2 7 9
pd.read_csv(StringIO(data),usecols=lambda x: x.upper() not in ['A','C'])
out:
b d
0 2 foo
1 5 bar
2 8 baz
Comments and Empty Lines
Ignoring Line Comments And Empty Lines
如果指定了comment参数,则将忽略注释行。 默认情况下,空行也将被忽略。
data = ('\n'
'a,b,c\n'
' \n'
'# commented line\n'
'1,2,3\n'
'\n'
'4,5,6')
print(data)
out:
a,b,c
# commented line
1,2,3
4,5,6
pd.read_csv(StringIO(data),comment='#')
out:
a b c
0 1 2 3
1 4 5 6
如果skip_blank_lines = False,则read_csv将不会忽略空行:
pd.read_csv(StringIO(data),comment='#',skip_blank_lines=False)
out:
a b c
NaN NaN
1 2 3
NaN NaN NaN
4 5 6
警告:被忽略的行的存在可能会导致涉及行号的歧义; 参数标题使用行号(忽略注释/空行),而行首使用行号(包括注释/空行).
data = ('#comment\n'
'a,b,c\n'
'A,B,C\n'
'1,2,3')
pd.read_csv(StringIO(data),comment='#',header=1)
out:
A B C
0 1 2 3
pd.read_csv(StringIO(data),comment='#',skiprows=2)
A B C
0 1 2 3
如果同时指定了skiprows和header,则header将相对于skiprows的末尾。 例如:
data = ('# empty\n'
'# second empty line\n'
'# third emptyline\n'
'X,Y,Z\n'
'1,2,3\n'
'A,B,C\n'
'1,2.,4.\n'
'5.,NaN,10.0\n')
print(data)
out:
# empty
# second empty line
# third emptyline
X,Y,Z
1,2,3
A,B,C
1,2.,4.
5.,NaN,10.0
pd.read_csv(StringIO(data),comment='#',skiprows=4,header=1)
out:
A B C
0 1.0 2.0 4.0
1 5.0 NaN 10.0
pd.read_csv(StringIO(data),skiprows=4,header=1)
out:
A B C
0 1.0 2.0 4.0
1 5.0 NaN 10.0
Comments
tmp = ('ID,level,category\n'
'Patient1,123000,x # really unpleasant\n'
'Patient2,23000,y # wouldn\'t take his medicine\n'
'Patient3,1234018,z # awesome')
pd.read_csv(StringIO(tmp))
out:
ID level category
0 Patient1 123000 x # really unpleasant
1 Patient2 23000 y # wouldn't take his medicine
2 Patient3 1234018 z # awesome
pd.read_csv(StringIO(tmp),comment='#')
out:
ID level category
0 Patient1 123000 x
1 Patient2 23000 y
2 Patient3 1234018 z
Index Columns And Trailing Delimiters
data = ('a,b,c\n'
'4,apple,bat,5.7\n'
'8,orange,cow,10')
pd.read_csv(StringIO(data))
out:
a b c
4 apple bat 5.7
8 orange cow 10.0
pd.read_csv(StringIO(data),index_col=0)
out:
a b c
4 apple bat 5.7
8 orange cow 10.0
data = ('a,b,c\n'
'4,apple,bat\n'
'8,orange,cow')
pd.read_csv(StringIO(data))
out:
a b c
0 4 apple bat
1 8 orange cow
pd.read_csv(StringIO(data),index_col=0)
out:
b c
a
4 apple bat
8 orange cow
pd.read_csv(StringIO(data),usecols=['b','c'])
out:
b c
0 apple bat
1 orange cow
pd.read_csv(StringIO(data),usecols=['b','c'],index_col=0)
out:
c
b
apple bat
orange cow
Date Handling
Specifying Date Columns
为了更好地使用日期时间数据,read_csv()使用关键字参数parse_dates和date_parser允许用户指定列的日期/时间格式,将string转换为日期时间对象。
foo = ('date,A,B,C\n'
'2009-01-01,a,1,2\n'
'2009-01-02,b,3,4\n'
'2009-01-03,c,4,5\n')
pd.read_csv(StringIO(foo),index_col=0,parse_dates=True)
out:
A B C
date
2009-01-01 a 1 2
2009-01-02 b 3 4
2009-01-03 c 4 5
pd.read_csv(StringIO(foo),index_col=0,parse_dates=True).index
DatetimeIndex(['2009-01-01', '2009-01-02', '2009-01-03'], dtype='datetime64[ns]', name='date', freq=None)
通常,我们可能希望分别存储日期和时间数据,或分别存储各种日期字段。 parse_dates关键字可用于指定列的组合,以从中解析日期和/或时间。 您可以指定要parse_dates的列或嵌套列表,结果日期列将被添加到输出的前面(以便不影响现有的列顺序),新的列名为各列Name的连接。
tmp = ('KORD,19990127, 19:00:00, 18:56:00, 0.8100\n'
'KORD,19990127, 20:00:00, 19:56:00, 0.0100\n'
'KORD,19990127, 21:00:00, 20:56:00, -0.5900\n'
'KORD,19990127, 21:00:00, 21:18:00, -0.9900\n'
'KORD,19990127, 22:00:00, 21:56:00, -0.5900\n'
'KORD,19990127, 23:00:00, 22:56:00, -0.5900')
pd.read_csv(StringIO(tmp),header=None,parse_dates=[[1,2],[1,3]])
out:
1_2 1_3 0 4
0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59
默认情况下,解析器会删除组件日期列,可以选择通过keep_date_col关键字保留它们:
pd.read_csv(StringIO(tmp),header=None,parse_dates=[[1,2],[1,3]],keep_date_col=True)
out:
1_2 1_3 0 1 2 3 4
0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 19990127 19:00:00 18:56:00 0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 19990127 20:00:00 19:56:00 0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD 19990127 21:00:00 20:56:00 -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD 19990127 21:00:00 21:18:00 -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD 19990127 22:00:00 21:56:00 -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD 19990127 23:00:00 22:56:00 -0.59
请注意,如果您希望将多个列合并为一个日期列,则必须使用嵌套列表。 换句话说,parse_dates = [1,2]表示第二和第三列应分别解析为单独的日期列,而parse_dates = [[1,2]]意味着应将这两列解析为单个列。
还可以使用字典来指定自定义名称列:
date_spec = {'nominal':[1,2],'actual':[1,3]}
pd.read_csv(StringIO(tmp),header=None,parse_dates=date_spec)
out:
nominal actual 0 4
0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59
重要的是要记住,如果要将多个文本列解析为单个日期列,则在数据前添加一个新列。
index_col参数基于这组新列而不是原始数据列:
pd.read_csv(StringIO(tmp),header=None,parse_dates=date_spec,index_col=0)
out:
actual 0 4
nominal
1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81
1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01
1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59
1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99
1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59
1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59
注意:如果列或索引包含不可解析的日期,则整个列或索引将作为对象数据类型原样返回。 对于非标准日期时间解析,请在pd.read_csv之后使用to_datetime()。
注意:read_csv具有用于解析iso8601格式的日期时间字符串的fast_path,例如“ 2000-01-01T00:01:02 + 00:00”和类似的变体。 如果可以安排数据以这种格式存储日期时间,则加载时间将明显缩短,约20倍。
Date Parsing Functions
最后,解析器允许您指定自定义date_parser函数,以充分利用日期解析API的灵活性:
pd.read_csv(StringIO(tmp),header=None,parse_dates=date_spec,
date_parser=pd.io.date_converters.parse_date_time)
out:
nominal actual 0 4
0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59
Parsing A Csv with Mixed Timezones
Pandas不能原生表示具有混合时区的列或索引。 如果CSV文件包含带有时区混合的列,则默认结果将是带有字符串的object-dtype列,即使包含parse_dates。
content = ('a\n'
'2000-01-01T00:00:00+05:00\n'
'2000-01-01T00:00:00+06:00\n')
pd.read_csv(StringIO(content),parse_dates=['a'])
out:
a
0 2000-01-01 00:00:00+05:00
1 2000-01-01 00:00:00+06:00
要将混合时区值解析为datetime列,请将部分应用的to_datetime()传递给utc = True作为date_parser。
pd.read_csv(StringIO(content),parse_dates=['a'],
date_parser=lambda col:pd.to_datetime(col,utc=True))
out:
a
0 1999-12-31 19:00:00+00:00
1 1999-12-31 18:00:00+00:00