1. 程式人生 > >【Python實戰】Pandas:讓你像寫SQL一樣做資料分析(一)

【Python實戰】Pandas:讓你像寫SQL一樣做資料分析(一)

1. 引言

Pandas是一個開源的Python資料分析庫。Pandas把結構化資料分為了三類:

  • Series,1維序列,可視作為沒有column名的、只有一個column的DataFrame;
  • DataFrame,同Spark SQL中的DataFrame一樣,其概念來自於R語言,為多column並schema化的2維結構化資料,可視作為Series的容器(container);
  • Panel,為3維的結構化資料,可視作為DataFrame的容器;

DataFrame較為常見,因此本文主要討論內容將為DataFrame。DataFrame的生成可通過讀取純文字、Json等資料來生成,亦可以通過Python物件來生成:

import pandas as pd
import numpy as np


df = pd.DataFrame({'total_bill': [16.99, 10.34, 23.68, 23.68, 24.59],
                   'tip': [1.01, 1.66, 3.50, 3.31, 3.61],
                   'sex': ['Female', 'Male', 'Male', 'Male', 'Female']})

對於DataFrame,我們可以看到其固有屬性:

# data type of columns
df.dtypes
# indexes
df.index
# return pandas.Index
df.columns
# each row, return array[array]
df.values
# a tuple representing the dimensionality of df
df.shape
  • .index,為行索引
  • .columns,為列名稱(label)
  • .dtype,為列資料型別

2. SQL操作

官方Doc給出了部分SQL的Pandas實現,在此基礎上本文給出了一些擴充說明。以下內容基於Python 2.7 + Pandas 0.18.1的版本。

select

SQL中的select是根據列的名稱來選取;Pandas則更為靈活,不但可根據列名稱選取,還可以根據列所在的position選取。相關函式如下:

  • loc,基於列label,可選取特定行(根據行index);
  • iloc,基於行/列的position;
df.loc[1:3, ['total_bill', 'tip']]
df.loc[1:3, 'tip': 'total_bill']
df.iloc[1:3, [1, 2]]
df.iloc[1:3, 1: 3]
  • at,根據指定行index及列label,快速定位DataFrame的元素;
  • iat,與at類似,不同的是根據position來定位的;
df.at[3, 'tip']
df.iat[3, 1]
  • ix,為loc與iloc的混合體,既支援label也支援position;
df.ix[1:3, [1, 2]]
df.ix[1:3, ['total_bill', 'tip']]

此外,有更為簡潔的行/列選取方式:

df[1: 3]
df[['total_bill', 'tip']]
# df[1:2, ['total_bill', 'tip']]  # TypeError: unhashable type

where

Pandas實現where filter,較為常用的辦法為df[df[colunm] boolean expr],比如:

df[df['sex'] == 'Female']
df[df['total_bill'] > 20]

# or
df.query('total_bill > 20')

在where子句中常常會搭配and, or, in, not關鍵詞,Pandas中也有對應的實現:

# and
df[(df['sex'] == 'Female') & (df['total_bill'] > 20)]
# or
df[(df['sex'] == 'Female') | (df['total_bill'] > 20)]
# in
df[df['total_bill'].isin([21.01, 23.68, 24.59])]
# not
df[-(df['sex'] == 'Male')]
df[-df['total_bill'].isin([21.01, 23.68, 24.59])]
# string function
df = df[(-df['app'].isin(sys_app)) & (-df.app.str.contains('^微信\d+$'))]

對where條件篩選後只有一行的dataframe取其中某一列的值,其兩種實現方式如下:

total = df.loc[df['tip'] == 1.66, 'total_bill'].values[0]
total = df.get_value(df.loc[df['tip'] == 1.66].index.values[0], 'total_bill')

distinct

df.drop_duplicates(subset=['sex'], keep='first', inplace=True)

包含引數:

  • subset,為選定的列做distinct,預設為所有列;
  • keep,值選項{'first', 'last', False},保留重複元素中的第一個、最後一個,或全部刪除;
  • inplace ,預設為False,返回一個新的dataframe;若為True,則返回去重後的原dataframe

group

group一般會配合合計函式(Aggregate functions)使用,比如:count、avg等。Pandas對合計函式的支援有限,有count和size函式實現SQL的count:

df.groupby('sex').size()
df.groupby('sex').count()
df.groupby('sex')['tip'].count()

對於多合計函式,

select sex, max(tip), sum(total_bill) as total
from tips_tb
group by sex;

實現在agg()中指定dict:

df.groupby('sex').agg({'tip': np.max, 'total_bill': np.sum})

# count(distinct **)
df.groupby('tip').agg({'sex': pd.Series.nunique})

as

SQL中使用as修改列的別名,Pandas也支援這種修改:

# first implementation
df.columns = ['total', 'pit', 'xes']
# second implementation
df.rename(columns={'total_bill': 'total', 'tip': 'pit', 'sex': 'xes'}, inplace=True)

其中,第一種方法的修改是有問題的,因為其是按照列position逐一替換的。因此,我推薦第二種方法。

join

Pandas中join的實現也有兩種:

# 1.
df.join(df2, how='left'...)

# 2. 
pd.merge(df1, df2, how='left', left_on='app', right_on='app')

第一種方法是按DataFrame的index進行join的,而第二種方法才是按on指定的列做join。Pandas滿足left、right、inner、full outer四種join方式。

order

Pandas中支援多列order,並可以調整不同列的升序/降序,有更高的排序自由度:

df.sort_values(['total_bill', 'tip'], ascending=[False, True])

top

對於全域性的top:

df.nlargest(3, columns=['total_bill'])

對於分組top,MySQL的實現(採用自join的方式):

select a.sex, a.tip
from tips_tb a
where (
    select count(*)
    from tips_tb b
    where b.sex = a.sex and b.tip > a.tip
) < 2
order by a.sex, a.tip desc;

Pandas的等價實現,思路與上類似:

# 1.
df.assign(rn=df.sort_values(['total_bill'], ascending=False)
          .groupby('sex')
          .cumcount()+1)\
    .query('rn < 3')\
    .sort_values(['sex', 'rn'])
    
# 2.
df.assign(rn=df.groupby('sex')['total_bill']
          .rank(method='first', ascending=False)) \
    .query('rn < 3') \
    .sort_values(['sex', 'rn'])

replace

replace函式提供對dataframe全域性修改,亦可通過where條件進行過濾修改(搭配loc):

# overall replace
df.replace(to_replace='Female', value='Sansa', inplace=True)

# dict replace
df.replace({'sex': {'Female': 'Sansa', 'Male': 'Leone'}}, inplace=True)

# replace on where condition 
df.loc[df.sex == 'Male', 'sex'] = 'Leone'

自定義

除了上述SQL操作外,Pandas提供對每列/每一元素做自定義操作,為此而設計以下三個函式:

  • map(func),為Series的函式,DataFrame不能直接呼叫,需取列後再呼叫;
  • apply(func),對DataFrame中的某一行/列進行func操作;
  • applymap(func),為element-wise函式,對每一個元素做func操作
df['tip'].map(lambda x: x - 1)
df[['total_bill', 'tip']].apply(sum)
df.applymap(lambda x: x.upper() if type(x) is str else x)

3. 實戰

環比增長

現有兩個月APP的UV資料,要得到月UV環比增長;該操作等價於兩個Dataframe left join後按指定列做減操作:

def chain(current, last):
    df1 = pd.read_csv(current, names=['app', 'tag', 'uv'], sep='\t')
    df2 = pd.read_csv(last, names=['app', 'tag', 'uv'], sep='\t')
    df3 = pd.merge(df1, df2, how='left', on='app')
    df3['uv_y'] = df3['uv_y'].map(lambda x: 0.0 if pd.isnull(x) else x)
    df3['growth'] = df3['uv_x'] - df3['uv_y']
    return df3[['app', 'growth', 'uv_x', 'uv_y']].sort_values(by='growth', ascending=False)

差集

對於給定的列,一個Dataframe過濾另一個Dataframe該列的值;相當於集合的差集操作:

def difference(left, right, on):
    """
    difference of two dataframes
    :param left: left dataframe
    :param right: right dataframe
    :param on: join key
    :return: difference dataframe
    """
    df = pd.merge(left, right, how='left', on=on)
    left_columns = left.columns
    col_y = df.columns[left_columns.size]
    df = df[df[col_y].isnull()]
    df = df.ix[:, 0:left_columns.size]
    df.columns = left_columns
    return df

相關推薦

Python實戰PandasSQL一樣資料分析

1. 引言 Pandas是一個開源的Python資料分析庫。Pandas把結構化資料分為了三類: Series,1維序列,可視作為沒有column名的、只有一個column的DataFrame; DataFrame,同Spark SQL中的DataFrame一樣,其概念來自於R語言,為多column並sch

Python實戰PandasSQL一樣資料分析

1. 引言 前一篇介紹了Pandas實現簡單的SQL操作,本篇中將主要介紹一些相對複雜一點的操作。為了方便後面實操,先給出一份簡化版的裝置統計資料: 0 android NLL 387546520 2099457911 0 ios NLL 52877990 916421755 1 and

實戰玩轉pandas資料分析——使用者消費行為分析python

  CD商品訂單資料的分析總結。根據訂單資料(使用者的消費記錄),從時間維度和使用者維度,分析該網站使用者的消費行為。通過此案例,總結訂單資料的一些共性,能通過使用者的消費記錄挖掘出對業務有用的資訊。對其他產品的線上消費資料分析有一定的借鑑價值,能達到舉一反三的效果。 訂單交易資料分析 [

Markdown費事?Typoraword一樣行雲流水,所見即所得。

Typora 簡介 Typora刪除了預覽視窗,以及所有其他不必要的干擾。取而代之的是實時預覽。 Markdown的語法因不同的解析器或編輯器而異,Typora使用的是GitHub Flavored Markdown。 下載 Typora下載。 常用快捷鍵 加粗: 

利用python/pandas/numpy資料分析-透視表pivot_table

透視表,根據一個或多個鍵進行聚合,並根據行列上的分組鍵將資料分配到各個矩形區域中. import numpy as np data=pd.DataFrame(np.arange(6).reshape((2,3)), index

ML專案基於網路爬蟲和資料探勘演算法的web招聘資料分析——資料獲取與處理

前言 這個專案是在學校做的,主要是想對各大招聘網站的招聘資料進行分析,沒準能從中發現什麼,這個專案週期有些長,以至於在專案快要結束時發現網上已經有了一些相關的專案,我後續會把相關的專案材料放在我的GitHub上面,連結為:https://github.com/

bigdata資料分析Java環境配置

Java環境 1.下載jdk(用FileZilla工具連線伺服器後上傳到需要安裝的目錄) 在 /opt/deploy 下新建 java 資料夾:  # mkdir / opt/deploy /java   解壓命令:tar zxvf 壓縮包名稱 (例如:tar zxvf jdk-8u191-

利用python資料分析-reindex

DataFrame.reindex(index=None, columns=None, **kwargs) reindex 函式的引數 引數 說明 method 插值填充方法 fill_value 引入的缺失資料值

Python股市資料分析

AAPL GOOG MSFT Date 2016-01-04 1.000000 1.000000 1.000000 2016-01-05 0.974941 1.000998 1.004562 2016-01-06 0.955861 1.002399 0.986314 201

MongoDb入門15分鐘敢說自己會用MongoDB了

一.MongDB是什麼呢,我該如何下手呢? MongoDB是一個基於分散式檔案儲存的資料庫。由C++語言編寫。旨在為WEB應用提供可擴充套件的高效能資料儲存解決方案。 如果小夥伴你的機器上還沒有安裝MongoDb的話請快快去安裝吧,下載地址:https://www.mongodb.com/download

Python實戰用Scrapyd把Scrapy爬蟲步部署到騰訊雲

將我們的爬蟲部署到騰訊雲伺服器上面。廢話不多說,我們就來實戰操作吧。 這裡選擇什麼雲服務都是可以的,阿里雲,AWS,騰訊雲,其他雲都是沒有問題的。部署方法基本一樣,這裡為了方便,所以筆者選擇了騰訊雲來做講解。 既然我們選擇了騰訊雲,首先去騰訊雲的官網,註冊登入一下。 點選複製https:

7個Python實戰專案程式碼,分分鐘晉級大神!

關於Python有一句名言:不要重複造輪子。 但是問題有三個: 1、你不知道已經有哪些輪子已經造好了,哪個適合你用。有名有姓的的著名輪子就400多個,更別說沒名沒姓自己在製造中的輪子。 2、確實沒重複造輪子,但是在重複製造汽車。包括好多大神寫的好幾百行程式碼,為的是

保險專題一則故事輕鬆瞭解保險的實質

正文:        100個學徒工來到一家五星級大酒店學習廚藝,他們要勤勤懇懇學習十年才能出師。學徒們的薪水不高,一年只有幾百塊,但是五星級酒店的餐具都非常名貴,一個盤子要1000塊錢。如果哪個學徒不小心打壞了一個盤子,那麼他不僅要傾家蕩產來賠償這1000元錢,還可

Python實戰機型自動化標註搜狗爬蟲實現

1. 引言 從安卓手機收集上來的機型大都為這樣: mi|5 mi|4c mi 4c 2014022 kiw-al10 nem-tl00h 收集的機型大都雜亂無章,不便於做統計分析。因此,標註顯得尤為重要。 中關村線上有對國內大部分手機的介紹情況,包括手機機型nem-tl00h及其對應的常見名稱榮耀暢玩5C

Python實戰Scrapy豌豆莢應用市場爬蟲

對於給定的大量APP,如何爬取與之對應的(應用市場)分類、描述的資訊?且看下面分解。 1. 頁面分析 當我們在豌豆莢首頁搜尋框輸入微信後,會跳轉到搜尋結果的頁面,其url為http://www.wandoujia.com/search?key=%微信。搜尋結果一般是按相關性排序的;所以,我們認為第一條搜尋結果

Python實戰Django建站筆記

前一段時間,用Django搭建一個報表分析的網站;藉此正好整理一下筆記。 1. 安裝 python有包管理工具pip,直接cd Python27/Scripts,輸入 pip install django # install by version pip install --upgrade Django==

7個Python實戰專案程式碼,感受下大神是如何起飛的!

關於Python有一句名言:不要重複造輪子。 但是問題有三個: 1、你不知道已經有哪些輪子已經造好了,哪個適合你用。有名有姓的的著名輪子就400多個,更別說沒名沒姓自己在製造中的輪子。 2、確實沒重複造輪子,但是在重複製造汽車。包括好多大神寫的好幾百行程式碼,為的是解決

pythonpython資料結構——線性表順序表的實現

前言 這一系列文章將介紹基於python語言的資料結構,主要涉及線性表、字串、棧和佇列、二叉樹和樹、圖、字典和集合、排序等。 線性表及分類 線性表是一類元素序列的抽象,是某類元素的集合,記錄著元素之間的順序關係。python中的list和tuple都支援線性表的需要,只是t

Unity Shaders使用CgInclude的Shader模組化——建立CgInclude檔案儲存光照模型

這裡是本書所有的插圖。這裡是本書所需的程式碼和資源(當然你也可以從官網下載)。========================================== 分割線 ==========================================寫在前面瞭解內建的C

Unity Shaders使用CgInclude的Shader模組化——Unity內建的CgInclude檔案

這裡是本書所有的插圖。這裡是本書所需的程式碼和資源(當然你也可以從官網下載)。========================================== 分割線 ==========================================寫在前面啦啦啦,又開