
本文來自于 猴子數(shù)據(jù)分析社群的通關(guān)作業(yè),因為課程是用 R 語言教的,我是用 Python 實現(xiàn)了一遍,所以參考的文檔也都列了出來,總結(jié)的也挺不容易的,歡迎同學(xué)吐槽。
- 數(shù)據(jù)來源與猴子聊人物的微信公眾號,這里也直接列出來方便一點的鏈接 https://pan.baidu.com/s/1qYFL8O8#list/path=%2F
文章主要簡單實現(xiàn)了一遍初級的數(shù)據(jù)分析過程,首先是利用 pandas 讀取 excel 文件,之后簡單的通過去空值等過程簡單處理了數(shù)據(jù)內(nèi)容,最后計算了了幾個常用的業(yè)績指標。
文章算是實現(xiàn)了一個最初級的數(shù)據(jù)分析過程,比起啃一本完整的教材還是不能實踐,不如今早開始將知識投入到使用過程中,形成宏觀知識架構(gòu)方便后續(xù)補充學(xué)習(xí),同時還能熟悉知識點和知識點之間的協(xié)作過程。
1. 讀取 excel
# coding=utf-8
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
file_name = "../源代碼和數(shù)據(jù)/朝陽醫(yī)院2016年銷售數(shù)據(jù).xlsx"
xls_file = pd.ExcelFile(file_name, dtype='object') # 統(tǒng)一先按照str讀入,之后轉(zhuǎn)換
table = xls_file.parse('Sheet1', dtype='object')
# file_name = "../源代碼和數(shù)據(jù)/朝陽醫(yī)院2016年銷售數(shù)據(jù).xlsx"
# table = pd.read_excel(file_name, sheeetname = 'Sheet1', dtype='object')
- 上面注釋掉的代碼是知乎網(wǎng)友 Dv Liu 提醒的另外一種讀取 excel 方法,在只讀取一個 sheet 的時候很方便。
https://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_excel.html](http://link.zhihu.com/?target=https%3A//pandas.pydata.org/pandas-docs/stable/generated/pandas.read_excel.html
print type(xls_file)
print type(table)
<class 'pandas.io.excel.ExcelFile'>
<class 'pandas.core.frame.DataFrame'>
table.head()
元數(shù)據(jù)好像沒有空值,所以我自己加了兩行

2. 數(shù)據(jù)預(yù)處理
2.1 查看基本信息
print table.shape
print table.index
print table.columns
(6579, 7)
RangeIndex(start=0, stop=6579, step=1)
Index([u'購藥時間', u'社??ㄌ?, u'商品編碼', u'商品名稱', u'銷售數(shù)量', u'應(yīng)收金額', u'實收金額'], dtype='object')
print table.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6579 entries, 0 to 6578
Data columns (total 7 columns):
購藥時間 6577 non-null object
社保卡號 6578 non-null object
商品編碼 6577 non-null object
商品名稱 6578 non-null object
銷售數(shù)量 6577 non-null object
應(yīng)收金額 6577 non-null object
實收金額 6577 non-null object
dtypes: object(7)
memory usage: 359.9+ KB
None
print table.count()
購藥時間 6577
社??ㄌ?6578
商品編碼 6577
商品名稱 6578
銷售數(shù)量 6577
應(yīng)收金額 6577
實收金額 6577
dtype: int64
2.2 列重命名
pandas 的 rename 方法 https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.rename.html
col = {u'購藥時間':'time',\
u'社??ㄌ?:'cardno',\
u'商品編碼':'drugId',\
u'商品名稱':'drugName',\
u'銷售數(shù)量':'saleNumber',\
u'應(yīng)收金額':'virtualmoney',\
u'實收金額':'actualmoney'}
table.rename(columns = col, inplace = True)
table.head()
2.3 刪除缺失值
pandas 的 dropna 方法 https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.dropna.html?highlight=dropna#pandas.DataFrame.dropna|
dropna1 = table.dropna()
dropna2 = table.dropna(how = 'all') # 參數(shù)設(shè)定是,所有值為 NA 才刪除掉
dropna1.head()

dropna2.head()

2.4 處理日期
- 一些列表推導(dǎo)式的參考內(nèi)容 https://docs.python.org/2.7/tutorial/datastructures.html?highlight=list%20comprehensions
- .to_datetime 方法 https://pandas.pydata.org/pandas-docs/stable/generated/pandas.to_datetime.html
# 定義一個把 time 行中日期和星期分開的函數(shù),分別返回日期和星期構(gòu)成的 list
def split_datetime_weekday(t_w_column):
datetime_list = [x.split()[0] for x in t_w_column ] # 列表推導(dǎo)式的簡寫
weekday_list = [x.split()[1] for x in t_w_column]
return datetime_list, weekday_list
datetime_list, weekday_list = split_datetime_weekday(dropna1.loc[:,'time'])
dropna1.loc[:,'datetime'] = pd.to_datetime(datetime_list)
# 這里直接用了一個 .to_datetime 方法,將所有數(shù)據(jù)的改成了 datetime64 的類型
dropna1.loc[:, 'weekday'] = weekday_list
dropna1.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 6577 entries, 2 to 6578
Data columns (total 9 columns):
time 6577 non-null object
cardno 6577 non-null object
drugId 6577 non-null object
drugName 6577 non-null object
saleNumber 6577 non-null object
virtualmoney 6577 non-null object
actualmoney 6577 non-null object
datetime 6577 non-null datetime64[ns]
weekday 6577 non-null object
dtypes: datetime64ns, object(8)
memory usage: 513.8+ KB
2.5 數(shù)據(jù)類型轉(zhuǎn)換
saleNumber virtualmoney actualmoney 三個 columns 的數(shù)據(jù)類型轉(zhuǎn)換
dropna1.loc[:,'saleNumber'] = dropna1['saleNumber'].astype('float64')
dropna1.loc[:,'virtualmoney'] = dropna1['virtualmoney'].astype('float64')
dropna1.loc[:,'actualmoney'] = dropna1['actualmoney'].astype('float64')
- 之前的改變數(shù)據(jù)類型的方式有報錯,還是改成了文檔推薦的賦值方式,兩種區(qū)別也簡要寫了一下,詳細可以閱讀官方文檔


http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
對比兩種索引形式 1. df[colom1][column2] 2. df.loc[index:column] 為什么 后者更好
- 是先索引第一層級[colom1],返回了 Dataframe 的對象,然后對這個對象再次索引 [column2],所以可以看做是一種連續(xù)兩次的線性操作
- loc 索引則是利用了一個組合的索引,pandas 可以把這個返回對象當(dāng)做一個整體處理,同時速度上也比第一種快。
dropna1.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 6577 entries, 2 to 6578
Data columns (total 9 columns):
time 6577 non-null object
cardno 6577 non-null object
drugId 6577 non-null object
drugName 6577 non-null object
saleNumber 6577 non-null float64
virtualmoney 6577 non-null float64
actualmoney 6577 non-null float64
datetime 6577 non-null datetime64[ns]
weekday 6577 non-null object
dtypes: datetime64ns, float64(3), object(5)
memory usage: 513.8+ KB
2.6 排序
按銷售時間對數(shù)據(jù)進行降序排列
老的 sort 方法好像已經(jīng)改名成為 sort_values 了,找了半天。https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.sort_values.html?highlight=sort_values#pandas.DataFrame.sort_values
DataFrame.sort_values(by, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last')
dropna1.sort_values("time").head(3)

3 簡單數(shù)據(jù)分析
本節(jié)是對一些指標的分析,我自己也加了一些可視化的內(nèi)容,可視化不是重點,只是簡單畫了一下,并沒有做美化,甚至連標簽都沒改。。
- 月均消費次數(shù)
- 月均消費金額
- 客單價
- 消費趨勢
3.1 月均消費次數(shù)
要點:
- 同一個日期和同一個社??ㄌ柕亩鄠€消費記錄算作一次消費,
- 可以將單獨兩列抽出來單獨分析,先去重復(fù),然后再計數(shù)
- 這個分析可以畫一個每個月的消費次數(shù)的折線圖
data_consume_unique 是將 datetime cardno 去重后復(fù)制出來的新的 Dataframe
data_consume_unique = dropna1.drop_duplicates(subset=['datetime', 'cardno']).copy(deep = True)
- drop_duplicates 的說明 https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.drop_duplicates.html 選取 datetime 和 cardno 作為篩選的子列
- consume_time_date_ser 是將 datetime 作為 index,構(gòu)建的 cardno 的時間序列。
- 注:構(gòu)建時間序列的內(nèi)容一定要轉(zhuǎn)換成列表。我自己也是探索了半天,用 Series 的數(shù)據(jù),填充的都是 NA。
consume_time_date_ser = pd.Series(list(data_consume_unique['cardno']), index = data_consume_unique['datetime'])
- 之前用了 sort_values 方法,果然也有 sort_index https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.sort_index.html,這里通過排序時間序列的 index,分別取出開始日期和結(jié)束日期
- timedelta 數(shù)據(jù)類型的 一些特性操作 https://docs.python.org/2.7/library/datetime.html?highlight=timedelta#datetime.timedelta,這里用了
.days提取出了時間區(qū)間的天數(shù)
# 排序之后將日期結(jié)尾 減去 日期開頭,計算總天數(shù)
date_interval = consume_time_date_ser.sort_index().index[-1]-consume_time_date_ser.sort_index().index[0]
# 利用總天使,計算總月數(shù),其中 timedelta 數(shù)據(jù)類型的 attribute 參見官方文檔
# 這里用到了 days,提取出了總天數(shù)
month_count = date_interval.days/30 + 1 # 我覺得這個月份應(yīng)該是要 + 1 的,猴子老師課程里面沒加
month_consume = consume_time_date_ser.count()/month_count
print month_consume
771
- 通過時間序列的 month 分組,之后用
.count()來計算組內(nèi)總和,也就是每月消費次數(shù)
month_time = consume_time_date_ser.groupby(consume_time_date_ser.index.month).count()
plt.plot(month_time.index, month_time)
plt.show()
3.2 月均消費金額
- 月均消費金額 = 總消費金額 / 月份數(shù)
- 這里可以畫一個每月消費總額的柱狀圖
total_money = dropna1['actualmoney'].sum()
month_money = total_money / month_count
print month_money # 43518.6085714
43518.6085714
- 構(gòu)建以實收金額 actualmoney 的時間序列 data_consume_actual
data_consume_actual = pd.Series(list(dropna1['actualmoney']), index = list(dropna1['datetime']))
- 通過 month 分組,對組內(nèi)數(shù)據(jù)進行求和,求和結(jié)果為每月的實收金額總和
month_consume = data_consume_actual.groupby(data_consume_actual.index.month).sum()
plt.plot(month_consume.index, month_consume)
plt.show()
3.3 客單價
- 客單價(per customer transaction)是指商場(超市)每一個顧客平均購買商品的金額,客單價也即是平均交易金額。
consume_num = len(dropna1['cardno'].unique())
print consume_num # 所有不重復(fù)醫(yī)??ㄌ柎a,總數(shù)量
pct = total_money / consume_num
print pct # 客單價
2426
125.568944765
3.4 消費趨勢
- 分組也是根據(jù) week 進行的分組,之后求和,類似于上面按照月的來求和
- 畫出隨著 week 變化與 實收金額 actualmoney 的變化趨勢
week_consume = data_consume_actual.groupby(data_consume_actual.index.week).sum()
plt.plot(week_consume.index, week_consume)
plt.show()
這個圖還是有點問題的,因為開頭幾天還歸屬2015年的周數(shù)計算,所以信息中有53周的數(shù)據(jù),折線也就直接拉到53周了。
與網(wǎng)友相關(guān)的關(guān)于切片,復(fù)制之類的討論:


歡迎關(guān)注我的微信公眾號 :practice_yuyang,不定期更新數(shù)據(jù)分析學(xué)習(xí)心得,學(xué)習(xí)過程。