Python模塊-pandas


數據分析雖說很多時候需要對業務和數據的理解,但其實大部分時候對數據的操作是相似(即使使用不同的工具,如Excel、Python、R等),像是數據清洗、表格結構修改、字段切分、分組計算等等。下面是使用Python中的Pandas包對數據分析常用操作的筆記。

數據讀取

pandas讀取文件后的數據集是一個DataFrame對象,該對象的每個列是一個Series對象

# pandas可讀取很多文件格式
# 但一般讀取數據的文件格式為:csv 和 excel
import pandas as pd
df = pd.read_csv("iris.csv",  
                 sep=',',
                 names=["A","B","C","D"],
                 nrows=2,  # 只要前兩行的數據
                 encoding='utf-8'
                )
df = pd.read_excel("iris.xlsx",
                   sheetname='XXXX',
                   header=0,  # 指定第一行為表頭
                   index_col=0,  # 指定第一列為索引
                   usecols = [0, 2]  # 只要第一和三列的數據
                  )
# 數據庫讀取
import pymysql # MySQL
import pymssql # SQLserver

conn = pymssql.connect(host='XXX.XX.XX.XX', user='username', password='123', database='DB')
OS = pd.read_sql("SELECT * FROM [KF_SZ].[dbo].[OSdepartrelation]",conn)
conn.close()

數據探索

# 查看數據量和特征量
df.shape
>>> (68630, 14)

# 查看數據集的頭5行
df.head()

# 查看數據集的尾5行
df.tail()

# 查看行名
df.index
>>> RangeIndex(start=0, stop=68630, step=1)

# 查看列名
df.columns
>>> Index(['行號', '倉庫', '貨號', '條碼', '商品名稱'], dtype='object')

# 幾乎用不上吧
df.values 

# 查看數據格式
df.dtypes
>>> 行號        int64
    倉庫       object
    貨號        int64
    條碼       object

# 計數:每個特征(列)的非空數量
df.count()
>>> 商品名稱    68630
    規格       6340
    單位      67719
    庫存數量    68630

# 計數:對單個列(Series)的頻數統計
df['倉庫'].value_counts()
>>> 公司總部總倉庫       2016
    佛山南海萬科店        938
    深圳寶安興業店        928
    深圳寶安百年店        907

# 返回唯一值的數組
df['區域'].unique()
>>> array(['深圳', '東莞', '廣州', '佛山', '江門', '成都', '四川'], dtype=object)    

# 統計描述:可以對整個數據集(DataFrame),也可以對單個列(Series)
df.describe()
df['庫存數量'].describe()
>>> count    68630.000000
    mean        19.545230
    std        248.819321
    min      -1600.000000
    25%          2.000000
    50%          5.000000
    75%         14.000000
    max      38080.000000
    Name: 庫存數量, dtype: float64
                   
# 小技巧
df.describe().astype(np.int64).T

數據清洗

數據清洗

# 單列字段清洗-去空格
df['商品名稱'] = df['商品名稱'].map(lambda s : s.strip())
df['A']=df['A'].map(str.strip)   # 去除兩邊空格
df['A']=df['A'].map(str.lstrip)  # 去除左邊空格
df['A']=df['A'].map(str.rstrip)  # 去除右邊空格
df['A']=df['A'].map(str.upper)   # 轉大寫
df['A']=df['A'].map(str.lower)   # 轉小寫
df['A']=df['A'].map(str.title)   # 首字母大寫

# 字段切分,並創建新特征
df.loc[:,"區域"] = df['倉庫'].map(lambda s:s[0:2])

類型轉換

# 轉換某特征(列)的數據格式
df['行號'] = df['行號'].astype(float)  

# 轉化時間格式
df['time']=pd.to_datetime(df['time'])

缺失值

  • 缺失值查看
# 缺失值判斷(在原數據對象以T/F替換)
df.isnull()
df.notnull()
df['A'].isnull()
  • 缺失值統計
# 缺失值計數方法
# 方法一
df['A'].isnull().value_counts()
>>> True     68629
    False        1
    Name: A, dtype: int64
# 方法二
df['A'].isnull().sum()
>>> 68629
df.isnull().sum()
>>> 倉庫          0
    貨號          0
    條碼          2
    規格      62290
  • 缺失值刪除
# 默認axi=0,how='any': 按行,任意一行有NaN就整列丟棄
df.dropna()
df.dropna(axis=1)

# 一行中全部為NaN的,才丟棄
df.driopna(how='all')

# 保留至少3個非空值的行:一行中有3個值是非空的就保留
df.dropna(thresh=3)
  • 缺失值填充
# 整個數據集填充
df.fillna(0)

# 有針對性的填充
df.fillna({'性別':'男', '年齡':30})

重復值

# 返回布爾向量、矩陣
df['A'].duplicated()
df.duplicated()
# 整個實例一模一樣才刪除,默認保留第一行
df.drop_duplicates()

# 保留k1列中的唯一值的行,默認保留第一行
df.drop_duplicates(subset=["k1"])

# 保留 k1和k2 組合的唯一值的行,take_last=True 保留最后一行
df.drop_duplicates(subset=["k1","k2"], take_last=True)

值替換

# 一對一替換
# 將df的A列中 -999 全部替換成空值
df["A"].replace(-999, np.nan)

# 多對一替換
# -999和1000 均替換成空值
obj.replace([-999,1000],  np.nan)

# 多對 一對一替換
# -999替換成空值,1000替換成0
obj.replace([-999,1000],  [np.nan, 0])
# 同上,寫法不同,更清晰
obj.replace({-999:np.nan, 1000:0})
# 有趣的寫法
dataset_raw.loc[dataset_raw['workclass'] == 'Without-pay', 'workclass'] = 'Not Working'

修改表結構

一般數據分析需要修改表結構都是在列上動手腳,注意操作有以下幾種

新增列

# 方式一
df['test'] = 0

# 方式二
df.loc[:,"區域"] = df['倉庫'].map(lambda s:s[0:2])

# 方式三
df.loc[:,"is_bonus"]=1
df.loc[df['remarks']=='無獎金', 'is_bonus'] = 0

# 方式四
# 需求:創建一個新變量test2 
# 1.petal_length>2 and petal_width>0.3 = 1 
# 2.sepeal_length>6 and sepal_width>3 = 2 3.其他 = 0
df.loc[(df['petal_length']>2)&(df['petal_width']>0.3), 'test2'] = 1
df.loc[(df['sepal_length']>6)&(df['sepal_width']>3), 'test2'] = 2

刪除列

# 丟棄指定的特征(列)
df.drop(['行號','條碼'],
        axis=1,
        inplace=True)

刪除行

# 刪除特色的行用得少,一般使用切片
df.drop(index=['no1','no2'],
        axis=0,
        inplace=True)

df.drop(df.index[[0,1]],
        axis=0,
        inplace=True)

修改列名

df.rename(columns = {'年':'compute_year',
                     '月/季度':'compute_month', 
                     '員工編號':'code', 
                     '員工姓名':'name', 
                     '職位':'position', 
                     '體系':'system_name',
                     '運營單位':'op_unit_name', 
                     '區域':'sub_area_name',
                     '部門名稱':'dept_name', 
                     '員工所屬小組': 'sub_dept_name'},
          inplace=True)

數據分組(數值變量)

# cut()數據分組,以連續值變量分組創建新特征
bins = [0, 5, 10, 15, 20]  # 切分的邊界
group_names = ['A', 'B', 'C', 'D']  # 每組的標簽名
df['new'] = pd.cut(df['old'], bins, labels=group_names) # new就是分組新特征

# qcut只要指定切分個數即可
df.qcut(df['年齡'],4)

數據分列(分類變量)

這個操作和Excel中的分列功能很像,在原始數據表中grade列中包含了兩個層級的用戶等級信息,現在我們通過數據分列將分級信息進行拆分。數據分列操作使用的是split函數,下面是具體的代碼和分列后的結果。

grade_split = pd.DataFrame((x.split('-') for x in loandata.grade),
                           index=loandata.index,
                           columns=['grade','sub_grade'])

完成數據分列操作后,使用merge函數將數據匹配會原始數據表,這個操作類似Excel中的Vlookup函數的功能。通過匹配原始數據表中包括了分列后的等級信息

loandata=pd.merge(loandata,grade_split,right_index=True, left_index=True)

設置索引

# 將列轉化為索引
# 將columns中的其中兩列:race和sex設置索引,race為一級,sex為二級
# inplace=True 在原數據集上修改的
# 默認情況下,設置成索引的列會從DataFrame中移除, drop=False將其保留下來
adult.set_index(['race','sex'], inplace = True) 

# 取消列索引設置,並自動填充索引
adult.reset_index(level=None, drop=Fasle, inplace=False)

# 索引重塑
df.stack()
df.unstack()

排序

df.b(by=['code'], ascending=False, na_position='first')
df.sort_values(by=['code', 'name'], ascending=False, na_position='first')
# 缺失值NaN默認是排在最后后的,na_position='first'設置為排在最前面

數據篩選/切片

  • []
  • isin
  • loc
  • iloc
  • ix
  • contains
# []只能對 行(row/index) 切片,前閉后開
df[0:3]
df[:4]
df[4:]

# where布爾查找,建立在[]基礎之上
df[df["A"]>7]
# 並
df.loc[(df['petal_length']>2)&(df['petal_width']>0.3)] 
# 或
df.loc[(df['petal_length']>2)|(df['petal_width']>0.3)] 
# isin()
# 返回布爾值
df["A"].isin([1,2,3])
df.loc[df['sepal_length'].isin([5.8,5.1])]
# loc :根據名稱Label切片
# df.loc[A,B] A是行范圍,B是列范圍
df.loc[:, ['petal_length','petal_width']]
df.loc[1:4, ['petal_length','petal_width']]
df.loc[['no1','no2'], ['petal_length','petal_width']]
# iloc:切位置,以序列號去切
df.iloc[1:4,:]
# ix:混切
# 名稱和位置混切,但效率低,少用
df1.ix[0:3,['sepal_length','petal_width']]
# contains()模糊匹配
# 使用DataFrame模糊篩選數據(類似SQL中的LIKE)
# 使用正則表達式進行模糊匹配,*匹配0或無限次,?匹配0或1次
df_obj[df_obj['套餐'].str.contains(r'.*?語音CDMA.*')] 
# 下面兩句效果一致
df[df['商品名稱'].str.contains("四件套")]
df[df['商品名稱'].str.contains(r".*四件套.*")]

正則表達式

多表拼接

merge 合並

  • pandas.merge可根據一個或多個鍵將不同DataFrame中的行合並起來

    # 在未指定連接鍵的情況下,merge會將重疊列的列名當做鍵
    pd.merge(left, right)
    
    # 指定“on”作為連接鍵,left和right兩個DataFrame必須同時存在“on”列,連接鍵也可N對N(少用)
    pd.merge(left, right, on="key")
    pd.merge(left, right, on=["key1", "key2"])
    
    # 指定left的連接鍵為“lkey”,right的連接鍵為“rkey”
    pd.merge(left,  right,  left_on="lkey",  right_on="rkey")
    
    # suffixes:用於追加到重疊列名的末尾,默認為("_x", "_y")
    pd.merge(left,  right,  on="key",  suffixes=("_left",  "_right"))
    
    # 指定連接方式:“inner”(默認),“left”,“right”,“outer”
    pd.merge(left, right, how="outer")
    
  • 多對多連接產生的是行的笛卡爾積

  • 常用方式:連接方式為“left”,right的連接鍵要唯一(去除重復值),通過right的數據補全left的數據索引上的合並(可用join代替,而且join更方便)

  • 當DataFrame的連接鍵位於其索引中,可以使用 left_index=True 和 right_index=True

    # 索引和索引連接
    pd.merge(left,  right,  left_index=True,  right_index=True)
    
    # "key"和索引連接
    pd.merge(left,  right,  left_on="key",  right_index=True)
    
    # 層次化索引
    pd.merge(left,  right,  left_on=["key1", "key2"],  right_index=True)
    

join 連接

  • DataFrame的join實例方法,是為了方便實現索引合並

    # 用left的索引和right的索引進行merge
    left.join(right)
    
    # 用left的索引和right的“key”進行merge
    left.join(right, on="key")
    
    # 層次化索引
    left.join(right, on=["key1", "key"])
    
    # join可以合並兩張以上的表,而merge只能合並兩張表
    left.join([right1, right2], how="outer")
    

concat 軸向連接

  • pandas.concat可以沿着一條軸將多個表對象堆疊到一起:因為模式how模式是“outer”

    # 默認 axis=0 上下拼接,列column重復的會自動合並
    pd.concat([df1, df2], axis=0)
    
    # axis=1 左右拼接,行raw/index重復的會自動合並
    pd.concat([df1, df2], axis=1)
    
    # 忽略df1和df2原來的index,重新給新的DataFrame設置從0開始的index
    pd.concat([df1,df2],  ignore_index=True)
    

append

  • 使用場景:表頭一致的多張表,進行連接(上下連接)

    df1.append(df2).append(df3)
    

combin_first 數據填補

  • 使用場景:有兩張表left和right,一般要求它們的表格結構一致,數據量也一致,使用right的數據去填補left的數據缺漏

  • 如果在同一位置left與right數據不一致,保留left的數據

    df1.combin_first(df2)
    

數據聚合&分組運算

groupby

# 單層分組
df.groupby('區域')
# 多層分組
df.groupby(['A','B'])

# 每個分組記錄的計數
df.groupby('區域').size()
>>> 區域
	東莞     7528
	中山      520
	佛山     5632
    ...
	dtype: int64
     
# 分組數
len(df.groupby('區域'))
>>> 7

aggregate

grouped = df.groupby(['A','B'])
# 對一個特征一次求得多個統計數
grouped['age'].agg([np.sum, np.mean, np.std])
# 對單一屬性統計可以改列名
grouped['age'].agg({"求和":np.sum,"求平均數":np.mean})
# 對不同屬性求不同的統計數
grouped.agg({'age':np.mean,'fnlwgt':np.sum})

filter

# filter()
# 過濾分組計數少於1000的分組,在把分組計數大於1000的分組整合成一個DataFrame返回
con1 = lambda s : len(s) > 1000
df1 = grouped.filter(con1)
# 過濾分組age均值小於30的分組
con2 = lambda s : s['age'].mean()>30
df2 = grouped3.filter(con2)

tansformation

# tansformation()
# 會返回一個數據集,這個數據集與group具有相同的索引以及大小 相當分組處理后合並
# 舉例說明對數據集進行標准化:
zscore = lambda s : (s - s.mean())/s.std()
df = grouped.transform(zscore)

數據透視表

crosstab

# crosstab() 一般只用與計數的數據透視表
pd.crosstab(index= df['A'],
            columns = [df['B'],df['C']],
            margins =True, 
            dropn=True)

pivot/pivot_table

# Produce 'pivot' table based on 3 columns of this DataFrame.
# Uses unique values from index / columns and fills with values.
# 感覺能使用的場景很少,因為不重復
df.pivot(index, columns, values)

df.pivot_table(values=None, 
               index=None, 
               columns=None, 
               aggfunc='mean', 
               fill_value=None, 
               margins=False, 
               dropna=True, 
               margins_name='All')
# 需求: index 是A ,columns 是 B,C, 我要求E的平均數並且有邊
pd.pivot_table(df,
               values = 'E',
               index = 'A',
               columns = ['B','C'],
               aggfunc = [np.mean,np.sum],
               margins = True)

時間序列

時間格式轉化

# 轉化時間格式
df['time']=pd.to_datetime(df['time'])

時間索引操作


啞編碼

pd.get_dummies(data, 
               prefix=None, 
               prefix_sep='_', 
               dummy_na=False, 
               columns=None, 
               sparse=False, 
               drop_first=False)

數據導出

pd.to_csv("XXX.csv",index=False)
from sqlalchemy import create_engine
engine = create_engine('mssql+pymssql://zxadmin:Zx!@#$8888@172.17.180.113/BS_KF')
pd.io.sql.to_sql(df,'payroll',engine,if_exists='append',index=False,chunksize=10000)

數據入庫

# pd 1.9以后的版本,除了sqllite,均需要通過sqlalchemy來設置
from sqlalchemy import create_engine
engine = create_engine('mssql+pymssql://zxadmin:Zx!@#$8888@172.17.180.113:8000/BS_KF')
# append:如果表存在,則將數據添加到這個表的后面
# fail:如果表存在就不操作
# replace:如果存在表,刪了,重建
pd.io.sql.to_sql(df,'table_name',engine,if_exists='append',index=False,chunksize=10000)
# 關閉連接
engine.dispose() 

技巧

數據集概覽

# 整體概況
def birdview(data):
    print(data.shape)
    d = {}
    for col in data.columns:
        if len(list(data[col].unique()))<30:
            d[col]= str(list(data[col].unique()))
        else:
            d[col]= "too much"

    r = pd.DataFrame(pd.Series(d),columns=['values'])\
            .join(pd.DataFrame(data.dtypes,columns=['type']))\
            .join(pd.DataFrame(data.count(),columns=['count']))\
            .join(pd.DataFrame(data.isnull().sum(),columns=['isnull']))
            
    return r.sort_values(by=['values','type',])
def catview(data):
    print("定性數據概況")
    for c in data.columns:
        print(c)
        print(dict(df[c].value_counts()))
def valueview(data):
    print("定值數據概況")
    return data.describe().T\
            .join(pd.DataFrame(data.skew(),columns=['skew']))\
            .join(pd.DataFrame(data.kurt(),columns=['kurt']))

長寬表轉換

寬表轉換為長表

# 方法一:先用set_index,再用stack,然后還有rename修改下列名
df.set_index(['Company','Name'], inplace = True).stack().reset_index()

# 方法二:melt
df.melt(id_vars=['Company','Name'],
        var_name='Year',
        value_name='Sale')

長表轉換為寬表

df.pivot_table(index=['Company','Name'], columns='Year', values='Sale')

行列互換

df.T


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM