1. 引言
前一篇介紹了Pandas實現簡單的SQL操作,本篇中將主要介紹一些相對復雜一點的操作。為了方便后面實操,先給出一份簡化版的設備統計數據:
0 android NLL 387546520 2099457911
0 ios NLL 52877990 916421755
1 android 魅族 8995958 120369597
1 android 酷派 9915906 200818172
1 android 三星 16500493 718969514
1 android 小米 23933856 290787590
1 android 華為 26706736 641907761
1 ios 蘋果 52877990 916421755
2 android 小米-小米4 2786675 55376581
2 android 魅族-m2-note 4642112 130984205
2 android OPPO-A31 4893428 62976997
2 ios 蘋果-iPhone-6s 5728609 99948716
其中,第一列表示維度組合編號,第二列表示操作系統類型,第三列為維度值(NLL表示缺失,即第一行、第二行表示操作系統的統計,其余表示廠商或機型),第三列、第四列分別表示UV、PV;且字段之間為\t
分隔。讀取該文件為DataFrame:
import pandas as pd
df = pd.read_csv(path, names=['id', 'os', 'dim', 'uv', 'pv'], sep='\t')
2. 實戰
Add
在原dataframe上,增加一行數據;可通過dataframe的append函數來追加:
import numpy as np
row_df = pd.DataFrame(np.array([['2', 'ios', '蘋果-iPad 4', 3287509, 32891811]]), columns=['id', 'os', 'dim', 'uv', 'pv'])
df = df.append(row_df, ignore_index=True)
增加一列數據,則比較簡單:
df['time'] = '2016-07-19'
To Dict
關於android、ios的PV、UV的dict:
def where(df, column_name, id_value):
df = df[df[column_name] == id_value]
return df
def to_dict(df):
"""
{"pv" or "uv" -> {"os": os_value}}
:return: dict
"""
df = where(df, 'id', 0)
df_dict = df.set_index('os')[['uv', 'pv']].to_dict()
return df_dict
Top
group某列后的top值,比如,android、ios的UV top 2的廠商:
def group_top(df, group_col, sort_col, top_n):
"""
get top(`sort_col`) after group by `group_col`
:param df: dataframe
:param group_col: string, column name
:param sort_col: string, column name
:param top_n: int
:return: dataframe
"""
return df.assign(rn=df.sort_values([sort_col], ascending=False)
.groupby(group_col)
.cumcount() + 1) \
.query('rn < ' + str(top_n + 1)) \
.sort_values([group_col, 'rn'])
全局top值加上group某列后的top值,並有去重:
def top(df, group_col, sort_col, top_n):
"""overall top and group top"""
all_top_df = df.nlargest(top_n, columns=sort_col)
grouped_top_df = group_top(df, group_col, sort_col, top_n)
grouped_top_df = grouped_top_df.ix[:, 0:-1]
result_df = pd.concat([all_top_df, grouped_top_df]).drop_duplicates()
return result_df
排序編號
對某列排序后並編號,相當於給出排序名次。比如,對UV的排序編號:
df['rank'] = df['uv'].rank(method='first', ascending=False).apply(lambda x: int(x))
Left Join
Pandas的left join對NULL的列沒有指定默認值,下面給出簡單的實現:
def left_join(left, right, on, right_col, default_value):
df = pd.merge(left, right, how='left', on=on)
df[right_col] = df[right_col].map(lambda x: default_value if pd.isnull(x) else x)
return df
自定義
對某一列做較為復雜的自定義操作,比如,廠商的UV占比:
def percentage(part, whole):
return round(100*float(part)/float(whole), 2)
os_dict = to_dict(df)
all_uv = sum(os_dict['uv'].values())
df = where(df, 'id', 1)
df['per'] = df.apply(lambda r: percentage(r['uv'], all_uv), axis=1)
重復值
某列的重復值的行:
duplicate = df.duplicated(subset=columns, keep=False)
寫MySQL
Pandas的to_sql函數支持Dataframe直接寫MySQL數據庫。在公司開發時,常常會有辦公網與研發網是不通的,Python的sshtunnel模塊提供ssh通道,便於入庫debug。
import MySQLdb
from sshtunnel import SSHTunnelForwarder
with SSHTunnelForwarder(('porxy host', port),
ssh_password='os passwd',
ssh_username='os user name',
remote_bind_address=('mysql host', 3306)) as server:
conn = MySQLdb.connect(host="127.0.0.1", user="mysql user name", passwd="mysql passwd",
db="db name", port=server.local_bind_port, charset='utf8')
df.to_sql(name='tb name', con=conn, flavor='mysql', if_exists='append', index=False)