[TOC]html
數據分析雖然說不少時候須要對業務和數據的理解,但其實大部分時候對數據的操做是類似(即便使用不一樣的工具,如Excel、Python、R等),像是數據清洗、表格結構修改、字段切分、分組計算等等。下面是使用Python中的Pandas包對數據分析經常使用操做的筆記。python
pandas讀取文件後的數據集是一個DataFrame對象,該對象的每一個列是一個Series對象mysql
# 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函數,下面是具體的代碼和分列後的結果。sql
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'設置爲排在最前面
# []只能對 行(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 合併app
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)
# 單層分組 df.groupby('區域') # 多層分組 df.groupby(['A','B']) # 每一個分組記錄的計數 df.groupby('區域').size() >>> 區域 東莞 7528 中山 520 佛山 5632 ... dtype: int64 # 分組數 len(df.groupby('區域')) >>> 7
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() # 過濾分組計數少於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() # 會返回一個數據集,這個數據集與group具備相同的索引以及大小 至關分組處理後合併 # 舉例說明對數據集進行標準化: zscore = lambda s : (s - s.mean())/s.std() df = grouped.transform(zscore)
# crosstab() 通常只用與計數的數據透視表 pd.crosstab(index= df['A'], columns = [df['B'],df['C']], margins =True, dropn=True)
# 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