1. 程式人生 > >ch6_01 Pandas 資料載入、儲存&檔案格式

ch6_01 Pandas 資料載入、儲存&檔案格式

  • 輸入輸出通常分為以下幾大類:讀取文字檔案和其他的更高效的磁碟儲存格式、載入資料庫中的資料、利用Web API操作網路資源

6.1讀寫文字格式的資料

  • pandas提供了一些用於將表格型資料讀取為DataFrame物件的函式。下表進行了總結,其中用的比較多的就是read_csv和read_table

6.1

import pandas as pd
df = pd.read_csv('data/ex1.csv')
df
a b c d message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
  • 雖然是.csv檔案,但是可以使用read_table來讀取,但是需要指定分隔符為“,”
pd.read_table('data/ex1.csv',sep=',')
d:\program filles\python\lib\site-packages\ipykernel_launcher.py:1: ParserWarning: Falling back to the 'python' engine because the separator encoded in utf-8 is > 1 char long, and the 'c' engine does not support such separators; you can avoid this warning by specifying engine='python'.
  """Entry point for launching an IPython kernel.
a,b,c,d,message
0 1,2,3,4,hello
1 5,6,7,8,world
2 9,10,11,12,foo
  • 並不是所有的檔案都有標題行,讀入檔案時可以使用預設的列名,也可以自己定義
pd.read_csv('data/ex2.csv',header=None)
0 1 2 3 4
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
pd.read_csv('data/ex2.csv',names=['A','B','C','D','message'])
A B C D message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
  • 如果想要明確的將該列放到索引4的位置上可以通過index_col指定message
names = ['a','b','c','d','message']
pd.read_csv('data/ex2.csv',names=names, index_col='message')
a b c d
message
hello 1 2 3 4
world 5 6 7 8
foo 9 10 11 12
  • 如果想要將多個列做成一個層次化索引,只需傳入由列編號或列名組成的列表即可
parsed = pd.read_csv('data/csv_mindex.csv',index_col=['key1','key2'])
parsed
value1 value2
key1 key2
one a 1 2
b 3 4
c 5 6
d 7 8
two a 9 10
b 11 12
c 13 14
d 15 16
  • 還有一些檔案含有不需要的資料,可以使用skiprows引數 跳過指定的行
result = pd.read_csv('data/ex4.csv',skiprows=[0,2,3])
result
a b c d message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
  • 缺失資料經常是要麼沒有(空字串),要麼用某個標記值表示。預設情況下,pandas會用一組經常出現的標記值進行識別,比如NA及NULL:
result = pd.read_csv('data/ex5.csv')
result
something a b c d message
0 one 1 2 3.0 4 NaN
1 two 5 6 NaN 8 world
2 three 9 10 11.0 12 foo
pd.isnull(result)
something a b c d message
0 False False False False False True
1 False False False True False False
2 False False False False False False
  • 引數na_values可以使用一個列表或者集合的字串表示缺失值
result = pd.read_csv('data/ex5.csv',na_values=['NULL'])
result
something a b c d message
0 one 1 2 3.0 4 NaN
1 two 5 6 NaN 8 world
2 three 9 10 11.0 12 foo
  • 字典的各列可以使用不同的NA標記值
sentinels = {'message':['foo','NA'],'something':['two']}
pd.read_csv('data/ex5.csv',na_values=sentinels)
something a b c d message
0 one 1 2 3.0 4 NaN
1 NaN 5 6 NaN 8 world
2 three 9 10 11.0 12 NaN
  • read_csv和read_table 一些常用的引數 引數列表 引數列表 引數列表

逐塊讀取文字檔案

  • 在處理很大的檔案時,或找出大檔案中的引數集以便於後續處理時,你可能只想讀取檔案的一小部分或逐塊對檔案進行迭代。
  • 在看大檔案之前,需要先設定pandas的一些引數
pd.options.display.max_rows = 8
result = pd.read_csv('data/examples/ex6.csv')
result
one two three four key
0 0.467976 -0.038649 -0.295344 -1.824726 L
1 -0.358893 1.404453 0.704965 -0.200638 B
2 -0.501840 0.659254 -0.421691 -0.057688 G
3 0.204886 1.074134 1.388361 -0.982404 R
... ... ... ... ... ...
9996 -0.479893 -0.650419 0.745152 -0.646038 E
9997 0.523331 0.787112 0.486066 1.093156 K
9998 -0.362559 0.598894 -1.843201 0.887292 G
9999 -0.096376 -1.012999 -0.657431 -0.573315 0

10000 rows × 5 columns

  • 如果之想讀取幾行,可以通過nrows進行指定即可:
pd.read_csv('data/examples/ex6.csv',nrows=5)
one two three four key
0 0.467976 -0.038649 -0.295344 -1.824726 L
1 -0.358893 1.404453 0.704965 -0.200638 B
2 -0.501840 0.659254 -0.421691 -0.057688 G
3 0.204886 1.074134 1.388361 -0.982404 R
4 0.354628 -0.133116 0.283763 -0.837063 Q
  • 如果要逐塊的讀取檔案,可以指定chunksize(行數)
chunker = pd.read_csv('data/examples/ex6.csv',chunksize=1000)
chunker
<pandas.io.parsers.TextFileReader at 0xed5ed0>
  • read_csv所返回的這個TextParser物件使你可以根據chunksize對檔案進行逐塊迭代。比如說,我們可以迭代處理ex6.csv,將值計數聚合到"key"列中,如下所示:
tot = pd.Series([])
for piece in chunker:
    tot = tot.add(piece['key'].value_counts(),fill_value=0)
tot = tot.sort_values(ascending=False)
tot[:10]
E    368.0
X    364.0
L    346.0
O    343.0
     ...  
J    337.0
F    335.0
K    334.0
H    330.0
Length: 10, dtype: float64

將資料寫出到文字格式

  • 資料也可以被輸出為分隔符格式的文字
data = pd.read_csv('data/examples/ex5.csv')
data
something a b c d message
0 one 1 2 3.0 4 NaN
1 two 5 6 NaN 8 world
2 three 9 10 11.0 12 foo
data.to_csv('data/out.csv')# 也可以傳入一個sep引數,指定分隔符
  • 缺失值在輸出結果中會被表示為空字串。也可將其表示為別的標記值
import sys
data.to_csv(sys.stdout,na_rep='NULL')
,something,a,b,c,d,message
0,one,1,2,3.0,4,NULL
1,two,5,6,NULL,8,world
2,three,9,10,11.0,12,foo
  • 如果沒有設定其他引數,則預設列出行和列的標籤。也可以不列出:
data.to_csv(sys.stdout,index=False,header=False)
one,1,2,3.0,4,
two,5,6,,8,world
three,9,10,11.0,12,foo
  • 也可以只列出一部分列,並指定輸出的順序
data.to_csv(sys.stdout,index=False,columns=['a','c','b'])
a,c,b
1,3.0,2
5,,6
9,11.0,10
  • Series也有一個to_csv 的方法
import numpy as np
dates = pd.date_range('1/1/2000',periods=7)
ts = pd.Series(np.arange(7),index = dates)
ts.to_csv('data/tseries.csv')
pd.read_csv('data/tseries.csv')
2000-01-01 0
0 2000-01-02 1
1 2000-01-03 2
2 2000-01-04 3
3 2000-01-05 4
4 2000-01-06 5
5 2000-01-07 6

處理分隔符格式

  • 大部分儲存在磁碟上的表格型資料都可以使用pandas.read_csv進行載入。但有時候需要做一些手工處理
import pandas as pd
import csv
f = open("data/examples/ex7.csv")
reader = csv.reader(f)
#對這個reader進行迭代將會為每行產生一個元組(並移除了所有的引號):
for line in reader:
    print(line)
['a', 'b', 'c']
['1', '2', '3']
['1', '2', '3']
  • 為了讓資料格式符合要求,需要做以下工作。首先讀取檔案到一個多行的列表中:
with open("data/examples/ex7.csv") as f:
    lines = list(csv.reader(f))
     #然後將這些行分為標題行和資料行
    header, values = lines[0], lines[1:]
    #然後,我們可以用字典構造式和zip(*values),後者將行轉置為列,建立資料列的字典:
    data_dict = {h:v for h,v in zip(header,zip(*values))}
 data_dict
{'a': ('1', '1'), 'b': ('2', '2'), 'c': ('3', '3')}
  • CSV檔案的形式有很多。只需定義csv.Dialect的一個子類即可定義出新格式(如專門的分隔符、字串引用約定、行結束符等):
class my_dialect(csv.Dialect):
    lineterminator = '\n'
    delimiter = '|'
    quotechar = '"'
    quoting = csv.QUOTE_MINIMAL

with open("data/examples/ex7.csv") as f:
    reader = csv.reader(f,dialect=my_dialect)
    print(reader)
<_csv.reader object at 0x07025DB0>
  • 各個CSV語支的引數也可以用關鍵字的形式提供給csv.reader,而無需定義子類:
with open("data/examples/ex7.csv") as f:
    reader = csv.reader(f,delimiter='|')
    print(reader)
<_csv.reader object at 0x07025D30>
  • 可用的選項(csv.Dialect的屬性)及其功能如表6-3所示

  • 要手工輸出分隔符檔案,你可以使用csv.writer。它接受一個已開啟且可寫的檔案物件以及跟csv.reader相同的那些語支和格式化選項:

with open('data/mydata.csv','w') as f:
    writer = csv.writer(f,dialect=my_dialect)
    writer.writerow(('one','two','three'))
    writer.writerow(('1','2','3'))
    writer.writerow(('4','5','6'))
    writer.writerow(('7','8','9'))

JSON 檔案

  • JSON(JavaScript Object Notation的簡稱)已經成為通過HTTP請求在Web瀏覽器和其他應用程式之間傳送資料的標準格式之一。它是一種比表格型文字格式(如CSV)靈活得多的資料格式。
obj = """
{"name": "Wes",
 "places_lived": ["United States", "Spain", "Germany"],
 "pet": null,
 "siblings": [{"name": "Scott", "age": 30, "pets": ["Zeus", "Zuko"]},
              {"name": "Katie", "age": 38,
               "pets": ["Sixes", "Stache", "Cisco"]}]
}
"""
  • 除其空值null和一些其他的細微差別(如列表末尾不允許存在多餘的逗號)之外, JSON非常接近於有效的Python程式碼。基本型別有物件(字典)、陣列(列表)、字串、數值、布林值以及null。 物件中所有的鍵都必須是字串。許多Python庫都可以讀寫JSON資料。我將使用json,因為它是構建於Python標準庫中的。通過json.loads即可將JSON字串轉換成Python形式:
import json
result = json.loads(obj)
result
{'name': 'Wes',
 'places_lived': ['United States', 'Spain', 'Germany'],
 'pet': None,
 'siblings': [{'name': 'Scott', 'age': 30, 'pets': ['Zeus', 'Zuko']},
  {'name': 'Katie', 'age': 38, 'pets': ['Sixes', 'Stache', 'Cisco']}]}
  • json.dumps則將Python物件轉換成JSON格式:
asjson = json.dumps(result)
asjson
'{"name": "Wes", "places_lived": ["United States", "Spain", "Germany"], "pet": null, "siblings": [{"name": "Scott", "age": 30, "pets": ["Zeus", "Zuko"]}, {"name": "Katie", "age": 38, "pets": ["Sixes", "Stache", "Cisco"]}]}'
  • 將(一個或一組)JSON物件轉換為DataFrame最簡單方便的方式是:向DataFrame構造器傳入一個字典的列表(就是原先的JSON物件),並選取資料欄位的子集:
siblings = pd.DataFrame(result['siblings'],columns=['name','age'])
siblings
name age
0 Scott 30
1 Katie 38
  • pandas.read_json可以自動將特別格式的JSON資料集轉換為Series或DataFrame。預設選項假設JSON陣列中的每個物件是表格中的一行:
data = pd.read_json('data/examples/example.json')
data
a b c
0 1 2 3
1 4 5 6
2 7 8 9
  • 將資料從pandas輸出到JSON,可以使用to_json方法:
print(data.to_json())
{"a":{"0":1,"1":4,"2":7},"b":{"0":2,"1":5,"2":8},"c":{"0":3,"1":6,"2":9}}
print(data.to_json(orient='records'))
[{"a":1,"b":2,"c":3},{"a":4,"b":5,"c":6},{"a":7,"b":8,"c":9}]

XML和HTML:Web資訊收集

  • Python有許多可以讀寫常見的HTML和XML格式資料的庫,包括lxml、Beautiful Soup和html5lib。lxml的速度比較快,但其它的庫處理有誤的HTML或XML檔案更好。pandas有一個內建的功能,read_html,它可以使用lxml和Beautiful Soup自動將HTML檔案中的表格解析為DataFrame物件
import pandas as pd
tables = pd.read_html('data/examples/fdic_failed_bank_list.html')
len(tables)
1
failures = tables[0]
failures.head()
Bank Name City ST CERT Acquiring Institution Closing Date Updated Date
0 Allied Bank Mulberry AR 91 Today's Bank September 23, 2016 November 17, 2016
1 The Woodbury Banking Company Woodbury GA 11297 United Bank August 19, 2016 November 17, 2016
2 First CornerStone Bank King of Prussia PA 35312 First-Citizens Bank & Trust Company May 6, 2016 September 6, 2016
3 Trust Company Bank Memphis TN 9956 The Bank of Fayette County April 29, 2016 September 6, 2016
4 North Milwaukee State Bank Milwaukee WI 20364 First-Citizens Bank & Trust Company March 11, 2016 June 16, 2016
  • 這裡可以先做一些資料清洗和統計
close_timestamps = pd.to_datetime(failures['Closing Date'])
close_timestamps.dt.year.value_counts()
2010    157
2009    140
2011     92
2012     51
2008     25
2013     24
2014     18
2002     11
2015      8
2016      5
2004      4
2001      4
2007      3
2003      3
2000      2
Name: Closing Date, dtype: int64

利用lxml.objectify解析XML

  • XML(Extensible Markup Language)是另一種常見的支援分層、巢狀資料以及元資料的結構化資料格式。介紹了pandas.read_html函式,XML和HTML的結構很相似,但XML更為通用。
from lxml import objectify
#先用lxml.objectify解析該檔案,然後通過getroot得到該XML檔案的根節點的引用
path = 'data/mta_perf/Performance_MNR.xml'
parsed = objectify.parse(open(path))
root = parsed.getroot()
#root.INDICATOR返回一個用於產生各個<INDICATOR>XML元素的生成器。
#對於每條記錄,我們可以用標記名(如YTD_ACTUAL)和資料值填充一個字典(排除幾個標記)
data = []
skip_fields = ['PARENT_SEQ', 'INDICATOR_SEQ','DESIRED_CHANGE', 'DECIMAL_PLACES']
for elt in root.INDICATOR:
    el_data = {}
    for child in elt.getchildren():
        if child.tag in skip_fields:
            continue
        el_data[child.tag] = child.pyval
    data.append(el_data)
# 最後將陣列字典轉換為dataFrame
perf = pd.DataFrame(data)
perf.head()
AGENCY_NAME CATEGORY DESCRIPTION FREQUENCY INDICATOR_NAME INDICATOR_UNIT MONTHLY_ACTUAL MONTHLY_TARGET PERIOD_MONTH PERIOD_YEAR YTD_ACTUAL YTD_TARGET
0 Metro-North Railroad Service Indicators Percent of commuter trains that arrive at thei... M On-Time Performance (West of Hudson) % 96.9 95 1 2008 96.9 95
1 Metro-North Railroad Service Indicators Percent of commuter trains that arrive at thei... M On-Time Performance (West of Hudson) % 95 95 2 2008 96 95
2 Metro-North Railroad Service Indicators Percent of commuter trains that arrive at thei... M On-Time Performance (West of Hudson) % 96.9 95 3 2008 96.3 95
3 Metro-North Railroad Service Indicators Percent of commuter trains that arrive at thei... M On-Time Performance (West of Hudson) % 98.3 95 4 2008 96.8 95
4 Metro-North Railroad Service Indicators Percent of commuter trains that arrive at thei... M On-Time Performance (West of Hudson) % 95.8 95 5 2008 96.6 95
from io import StringIO
tag = '<a href="http://www.google.com">Google</a>'
root  = objectify.parse(StringIO(tag)).getroot()
#接下來就可以訪問標籤或者連結文字中的任何欄位
root
<Element a at 0xcac8a0>
root.get('href')
'http://www.google.com'
root.text
'Google'