目前最常用的數據保存格式可能就是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:
caratcutcolorclaritydepthtablepricexyz
00.23IdealESI261.5553263.953.982.43
10.21PremiumESI159.8613263.893.842.31
20.23GoodEVS156.9653274.054.072.31
30.29PremiumIVS262.4583344.24.232.63
40.31GoodJSI263.3583354.344.352.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:
caratcutcolorclaritydepthtablepricexyz
80.22NaNEVS265.161.03373.873.782.49
910.86NaNESI255.169.027576.456.333.52
970.96NaNFSI266.362.027596.275.954.07
1230.70NaNFVS264.557.027625.575.533.58
1240.70NaNFVS265.355.027625.635.583.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:
abc
0123
1456
2789
通過指定name與header,可以重命名列以及是否丟棄標題行:
pd.read_csv(StringIO(data),names=['foo','bar','baz'],header=0)
out:
foobarbaz
0123
1456
2789
pd.read_csv(StringIO(data),names=['foo','bar','baz'],header=None)
out:
foobarbaz
0abc
1123
2456
3789
如果標題不在第一行中,則將行號傳遞給標題,將跳過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:
abc
0123
1456
2789
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:
aba.1
0012
1345
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:
abcd
0123foo
1456bar
2789baz
pd.read_csv(StringIO(data),usecols=['b','d'])
out:
bd
02foo
15bar
28baz
pd.read_csv(StringIO(data),usecols=[0,1,3])
out:
abd
012foo
145bar
278baz
pd.read_csv(StringIO(data),usecols=lambda x: x.upper() in ['A','C'])
out:
ac
013
146
279
pd.read_csv(StringIO(data),usecols=lambda x: x.upper() not in ['A','C'])
out:
bd
02foo
15bar
28baz
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:
abc
0123
1456
如果skip_blank_lines = False,則read_csv將不會忽略空行:
pd.read_csv(StringIO(data),comment='#',skip_blank_lines=False)
out:
abc
NaNNaN
123
NaNNaNNaN
456
警告:被忽略的行的存在可能會導致涉及行號的歧義; 參數標題使用行號(忽略注釋/空行),而行首使用行號(包括注釋/空行).
data = ('#comment\n'
'a,b,c\n'
'A,B,C\n'
'1,2,3')
pd.read_csv(StringIO(data),comment='#',header=1)
out:
ABC
0123
pd.read_csv(StringIO(data),comment='#',skiprows=2)
ABC
0123
如果同時指定了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:
ABC
01.02.04.0
15.0NaN10.0
pd.read_csv(StringIO(data),skiprows=4,header=1)
out:
ABC
01.02.04.0
15.0NaN10.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:
IDlevelcategory
0Patient1123000x # really unpleasant
1Patient223000y # wouldn't take his medicine
2Patient31234018z # awesome
pd.read_csv(StringIO(tmp),comment='#')
out:
IDlevelcategory
0Patient1123000x
1Patient223000y
2Patient31234018z
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:
abc
4applebat5.7
8orangecow10.0
pd.read_csv(StringIO(data),index_col=0)
out:
abc
4applebat5.7
8orangecow10.0
data = ('a,b,c\n'
'4,apple,bat\n'
'8,orange,cow')
pd.read_csv(StringIO(data))
out:
abc
04applebat
18orangecow
pd.read_csv(StringIO(data),index_col=0)
out:
bc
a
4applebat
8orangecow
pd.read_csv(StringIO(data),usecols=['b','c'])
out:
bc
0applebat
1orangecow
pd.read_csv(StringIO(data),usecols=['b','c'],index_col=0)
out:
c
b
applebat
orangecow
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:
ABC
date
2009-01-01a12
2009-01-02b34
2009-01-03c45
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_21_304
01999-01-27 19:00:001999-01-27 18:56:00KORD0.81
11999-01-27 20:00:001999-01-27 19:56:00KORD0.01
21999-01-27 21:00:001999-01-27 20:56:00KORD-0.59
31999-01-27 21:00:001999-01-27 21:18:00KORD-0.99
41999-01-27 22:00:001999-01-27 21:56:00KORD-0.59
51999-01-27 23:00:001999-01-27 22:56:00KORD-0.59
默認情況下,解析器會刪除組件日期列,可以選擇通過keep_date_col關鍵字保留它們:
pd.read_csv(StringIO(tmp),header=None,parse_dates=[[1,2],[1,3]],keep_date_col=True)
out:
1_21_301234
01999-01-27 19:00:001999-01-27 18:56:00KORD1999012719:00:0018:56:000.81
11999-01-27 20:00:001999-01-27 19:56:00KORD1999012720:00:0019:56:000.01
21999-01-27 21:00:001999-01-27 20:56:00KORD1999012721:00:0020:56:00-0.59
31999-01-27 21:00:001999-01-27 21:18:00KORD1999012721:00:0021:18:00-0.99
41999-01-27 22:00:001999-01-27 21:56:00KORD1999012722:00:0021:56:00-0.59
51999-01-27 23:00:001999-01-27 22:56:00KORD1999012723:00:0022: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:
nominalactual04
01999-01-27 19:00:001999-01-27 18:56:00KORD0.81
11999-01-27 20:00:001999-01-27 19:56:00KORD0.01
21999-01-27 21:00:001999-01-27 20:56:00KORD-0.59
31999-01-27 21:00:001999-01-27 21:18:00KORD-0.99
41999-01-27 22:00:001999-01-27 21:56:00KORD-0.59
51999-01-27 23:00:001999-01-27 22:56:00KORD-0.59
重要的是要記住,如果要將多個文本列解析為單個日期列,則在數據前添加一個新列。
index_col參數基于這組新列而不是原始數據列:
pd.read_csv(StringIO(tmp),header=None,parse_dates=date_spec,index_col=0)
out:
actual04
nominal
1999-01-27 19:00:001999-01-27 18:56:00KORD0.81
1999-01-27 20:00:001999-01-27 19:56:00KORD0.01
1999-01-27 21:00:001999-01-27 20:56:00KORD-0.59
1999-01-27 21:00:001999-01-27 21:18:00KORD-0.99
1999-01-27 22:00:001999-01-27 21:56:00KORD-0.59
1999-01-27 23:00:001999-01-27 22:56:00KORD-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:
nominalactual04
01999-01-27 19:00:001999-01-27 18:56:00KORD0.81
11999-01-27 20:00:001999-01-27 19:56:00KORD0.01
21999-01-27 21:00:001999-01-27 20:56:00KORD-0.59
31999-01-27 21:00:001999-01-27 21:18:00KORD-0.99
41999-01-27 22:00:001999-01-27 21:56:00KORD-0.59
51999-01-27 23:00:001999-01-27 22:56:00KORD-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
02000-01-01 00:00:00+05:00
12000-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
01999-12-31 19:00:00+00:00
11999-12-31 18:00:00+00:00
?```