Python數據分析示例(1)Day2

說明:本文章爲Python數據處理學習日誌,主要內容來自書本《利用Python進行數據分析》,Wes McKinney著,機械工業出版社。

“以我的觀點來看,如果只需要用Python進行高效的數據分析工作,根本就沒必要非得成爲通用軟件編程方面的專家不可。”——作者

接下來是書本一些代碼的實現,用來初步瞭解Python處理數據的功能,相關資源可在下方鏈接下載。
書本相關資源

讀取文件第一行

相關例子可以再shell中執行,也可以再Canopy Editor中執行。以下代碼在Canopy Editor中執行。

path='E:\\Enthought\\book\\ch02\\usagov_bitly_data2012-03-16-1331923249.txt'
open(path).readline()

在shell中執行則不要轉置符’\’,path爲存放文件路徑。在shell中執行一定要用pylab模式打開,不然繪圖會出現些小問題。

path='E:\Enthought\book\ch02\usagov_bitly_data2012-03-16-1331923249.txt'

輸出結果:

Out[2]: '{ "a": "Mozilla\\/5.0 (Windows NT 6.1; WOW64) AppleWebKit\\/535.11 (KHTML, like Gecko) Chrome\\/17.0.963.78 Safari\\/535.11", "c": "US", "nk": 1, "tz": "America\\/New_York", "gr": "MA", "g": "A6qOVH", "h": "wfLQtf", "l": "orofrog", "al": "en-US,en;q=0.8", "hh": "1.usa.gov", "r": "http:\\/\\/www.facebook.com\\/l\\/7AQEFzjSi\\/1.usa.gov\\/wfLQtf", "u": "http:\\/\\/www.ncbi.nlm.nih.gov\\/pubmed\\/22415991", "t": 1331923247, "hc": 1331822918, "cy": "Danvers", "ll": [ 42.576698, -70.954903 ] }\n'

以上結果爲純文本加載。
Python中的內置或者第三方模塊將JSON字符串轉化爲Python字典對象。這裏,將使用json模塊及其loads函數逐行加載已經下載好的數據文件:

import json
path='E:\\Enthought\\book\\ch02\\usagov_bitly_data2012-03-16-1331923249.txt'
records=[json.loads(line) for line in open(path)]
records[0]

這樣,records對象就成了一組Python字典了。

Out[6]: 
{u'a': u'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.78 Safari/535.11',
 u'al': u'en-US,en;q=0.8',
 u'c': u'US',
 u'cy': u'Danvers',
 u'g': u'A6qOVH',
 u'gr': u'MA',
 u'h': u'wfLQtf',
 u'hc': 1331822918,
 u'hh': u'1.usa.gov',
 u'l': u'orofrog',
 u'll': [42.576698, -70.954903],
 u'nk': 1,
 u'r': u'http://www.facebook.com/l/7AQEFzjSi/1.usa.gov/wfLQtf',
 u't': 1331923247,
 u'tz': u'America/New_York',
 u'u': u'http://www.ncbi.nlm.nih.gov/pubmed/22415991'}

現在,只要一字符串形式給出想要訪問的鍵就可以得到當前記錄中相應的值了:

records[0]['tz']
Out[7]: u'America/New_York'

print records[0]['tz']
America/New_York

對時區進行計數

1.用純Python對時區進行計數

假設我們想知道該數據集中最常出現的是哪個時區(即tz字段)。首先,我們用列表推導式取出一組時區:

time_zones=[rec['tz'] for rec in records if 'tz' in rec]
time_zones[:10] #獲取前10條記錄的tz

結果:

Out[12]: 
[u'America/New_York',
 u'America/Denver',
 u'America/New_York',
 u'America/Sao_Paulo',
 u'America/New_York',
 u'America/New_York',
 u'Europe/Warsaw',
 u'',
 u'',
 u'']

接下來對時區進行計數:

 def get_counts(sequence):
    counts={}
    for x in sequence:
        if x in counts:
            counts[x]+=1
        else:
            counts[x]=1
    return counts

或者使用Python標準庫

def get_counts2(sequence):
    counts=defaultdict(int) #所有初始值均會初始化爲0
    for x in sequence:
        counts[x]+=1
    return counts

調用函數

counts = get_counts(time_zones)

counts['America/New_York']
Out[20]: 1251

len(time_zones)
Out[21]: 3440

如果只想要得到前10位的時區及其計數值,我們需要用到一些關羽字典的處理技巧:

def top_counts(count_dict,n=10):
    value_key_pairs=[(count,tz) for tz,count in count_dict.items()]
    value_key_pairs.sort()
    return value_key_pairs[-n:]

數據處理結果

top_counts(counts)
Out[26]: 
[(33, u'America/Sao_Paulo'),
 (35, u'Europe/Madrid'),
 (36, u'Pacific/Honolulu'),
 (37, u'Asia/Tokyo'),
 (74, u'Europe/London'),
 (191, u'America/Denver'),
 (382, u'America/Los_Angeles'),
 (400, u'America/Chicago'),
 (521, u''),
 (1251, u'America/New_York')]

或者在Python標準庫中找到collections.Counter類,處理起來就比較簡單:

from collections import Counter
counts=Counter(time_zones)
counts.most_common(10)
Out[29]: 
[(u'America/New_York', 1251),
 (u'', 521),
 (u'America/Chicago', 400),
 (u'America/Los_Angeles', 382),
 (u'America/Denver', 191),
 (u'Europe/London', 74),
 (u'Asia/Tokyo', 37),
 (u'Pacific/Honolulu', 36),
 (u'Europe/Madrid', 35),
 (u'America/Sao_Paulo', 33)]

2.用pandas對時區進行計數

DataFrame是pandas中重要的數據結構,它用於將數據表示成一個表格。

from pandas import DataFrame,Series
import pandas as pd;import numpy as np
frame = DataFrame(records)

結果

frame['tz'][:10]
Out[34]: 
0     America/New_York
1       America/Denver
2     America/New_York
3    America/Sao_Paulo
4     America/New_York
5     America/New_York
6        Europe/Warsaw
7                     
8                     
9                     
Name: tz, dtype: object

frame[‘tz’]所返回的Series對象有一個value_counts方法,該方法可以讓我們得到所需要的信息:

tz_counts = frame['tz'].value_counts()
tz_counts[:10]
Out[36]: 
America/New_York       1251
                        521
America/Chicago         400
America/Los_Angeles     382
America/Denver          191
Europe/London            74
Asia/Tokyo               37
Pacific/Honolulu         36
Europe/Madrid            35
America/Sao_Paulo        33
Name: tz, dtype: int64

繪圖

然後,我們可以利用繪圖庫(即matplotlib)爲該段數據生成一張圖片。爲此,我們先給記錄中位未知或者缺失的時區填上一個替代值。fillna函數可以替換缺失值(NA),而未知值(空字符串)則可以通過布爾型數組索引加以替換:

clean_tz=frame['tz'].fillna('Missing')
clean_tz[clean_tz==''] = 'Unknown'
tz_counts=clean_tz.value_counts()
tz_counts[:10]
Out[41]: 
America/New_York       1251
Unknown                 521
America/Chicago         400
America/Los_Angeles     382
America/Denver          191
Missing                 120
Europe/London            74
Asia/Tokyo               37
Pacific/Honolulu         36
Europe/Madrid            35
Name: tz, dtype: int64

繪圖

tz_counts[:10].plot(kind='barh',rot=0)
Out[42]: <matplotlib.axes._subplots.AxesSubplot at 0xe50b1d0>

結果
示例數據中最常出現的時區

對Agent(‘a’)進行劃分

‘a’字段含有執行URL縮短操作的瀏覽器、設備、應用程序的相關信息:

frame['a'][0]
Out[43]: u'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.78 Safari/535.11'

運用Python內置的字符串函數和正則表達式,可以將這種字符串的第一節(與瀏覽器大致對應)分離出來並得到另外一份用戶行爲摘要:

results = Series([x.split()[0] for x in frame.a.dropna()]) #按空格切分字段'a'
results[:5]
Out[45]: 
0               Mozilla/5.0
1    GoogleMaps/RochesterNY
2               Mozilla/4.0
3               Mozilla/5.0
4               Mozilla/5.0
dtype: object
results.value_counts()[:8]
Out[46]: 
Mozilla/5.0                 2594
Mozilla/4.0                  601
GoogleMaps/RochesterNY       121
Opera/9.80                    34
TEST_INTERNET_AGENT           24
GoogleProducer                21
Mozilla/6.0                    5
BlackBerry8520/5.0.0.681       4
dtype: int64

現在,根據Windows用戶和非Windows用戶對時區統計信息進行分解。爲了方便起見,嘉定只要agent字符串中含有“Windows”就認爲該用戶爲Windows用戶。

cframe = frame[frame.a.notnull()] #刪除agent爲空的記錄
 operating_system = np.where(cframe['a'].str.contains('Windows'),'Windows','Not Windows')
operating_system[:5]
Out[52]: #可能是版本原因,此處略有不同,原書以frame形式輸出
array(['Windows', 'Not Windows', 'Windows', 'Not Windows', 'Windows'], 
      dtype='|S11')

接下來就可以根據時區和新得到的操作系統列表對數據進行分組,然後通過size對分組結果進行計數(類似於上面的value_counts函數),並利用unstack對技術結果進行重塑:

by_tz_os = cframe.groupby(['tz',operating_system])
agg_counts = by_tz_os.size().unstack().fillna(0)
agg_counts[:10]
Out[56]: 
                                Not Windows  Windows
tz                                                  
                                      245.0    276.0
Africa/Cairo                            0.0      3.0
Africa/Casablanca                       0.0      1.0
Africa/Ceuta                            0.0      2.0
Africa/Johannesburg                     0.0      1.0
Africa/Lusaka                           0.0      1.0
America/Anchorage                       4.0      1.0
America/Argentina/Buenos_Aires          1.0      0.0
America/Argentina/Cordoba               0.0      1.0
America/Argentina/Mendoza               0.0      1.0

最後,選出最常出現的時區。根據agg_counts中的行數構造一個間接索引數組:

indexer = agg_counts.sum(1).argsort()
indexer[:10]
Out[58]: 
tz
                                  24
Africa/Cairo                      20
Africa/Casablanca                 21
Africa/Ceuta                      92
Africa/Johannesburg               87
Africa/Lusaka                     53
America/Anchorage                 54
America/Argentina/Buenos_Aires    57
America/Argentina/Cordoba         26
America/Argentina/Mendoza         55
dtype: int64

然後通過take按照這個順序截取最後10行:

count_subset = agg_counts.take(indexer)[-10:]
count_subset
Out[60]: 
                     Not Windows  Windows
tz                                       
America/Sao_Paulo           13.0     20.0
Europe/Madrid               16.0     19.0
Pacific/Honolulu             0.0     36.0
Asia/Tokyo                   2.0     35.0
Europe/London               43.0     31.0
America/Denver             132.0     59.0
America/Los_Angeles        130.0    252.0
America/Chicago            115.0    285.0
                           245.0    276.0
America/New_York           339.0    912.0

生成圖形

count_subset.plot(kind='barh',stacked=True)
Out[61]: <matplotlib.axes._subplots.AxesSubplot at 0xeb89d68>

結果

爲了看清楚小分組中Windows用戶的相對比例,可以將各行規範化爲“總計爲1”並重新繪圖:

normed_subset = count_subset.div(count_subset.sum(1),axis=0)
normed_subset.plot(kind='barh',stacked=True)
Out[63]: <matplotlib.axes._subplots.AxesSubplot at 0xed1e1d0>

結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章