基本Data ETL

許sir

[標準開始動作]. 如果你用python的目標是科學計算或數值分析,基本是引⼊三個套件

In [1]:
%matplotlib inline 
##將後續畫圖的結果直接顯現在網頁中

import numpy as np 
import matplotlib.pyplot as plt 
import pandas as pd  #

[PART 1]. 輸入資料與資料格式

用 df 是標準的叫法 (雖然這名稱我們隨便取也可以), 意思是 Data Frame, 這是 pandas 兩大資料結構之一。我們可以把 Data Frame 想成一張表格 (雖然其實可以是很多張表格)。

我們來看看我們 df 的前五筆資料。

In [2]:
df = pd.read_csv("grades.csv") #這裡的df是 data-frame的意思,你也可以用其他名稱代替
df.head() #看前五筆資料
Out[2]:
姓名 國文 英文 數學 自然 社會
0 劉俊安 9 10 15 10 13
1 胡玉華 10 10 10 8 9
2 黃淑婷 13 15 8 11 14
3 陳上紫 10 10 8 9 14
4 崔靜成 13 12 14 12 13
In [3]:
df.describe() #基本統計量
Out[3]:
國文 英文 數學 自然 社會
count 100.000000 100.000000 100.000000 100.00000 100.00000
mean 11.390000 11.380000 11.570000 11.03000 11.83000
std 2.196853 2.273164 2.310516 2.21772 2.48655
min 8.000000 8.000000 8.000000 8.00000 8.00000
25% 9.000000 9.000000 10.000000 9.00000 9.00000
50% 11.000000 11.000000 11.000000 11.00000 12.00000
75% 13.000000 13.000000 14.000000 13.00000 14.00000
max 15.000000 15.000000 15.000000 15.00000 15.00000
In [4]:
df.corr() #相關係數
Out[4]:
國文 英文 數學 自然 社會
國文 1.000000 0.160158 -0.310899 -0.110236 -0.028421
英文 0.160158 1.000000 0.025656 0.113929 -0.063512
數學 -0.310899 0.025656 1.000000 0.014371 0.041651
自然 -0.110236 0.113929 0.014371 1.000000 -0.156594
社會 -0.028421 -0.063512 0.041651 -0.156594 1.000000
In [5]:
df[["國文", "英文", "數學"]].sum(1)#選三科相加
Out[5]:
0     34
1     30
2     36
3     28
4     39
5     37
6     34
7     27
8     30
9     37
10    31
11    35
12    37
13    38
14    31
15    29
16    34
17    36
18    25
19    34
20    35
21    28
22    36
23    35
24    37
25    40
26    33
27    35
28    37
29    38
      ..
70    37
71    30
72    32
73    43
74    33
75    30
76    36
77    34
78    38
79    31
80    40
81    32
82    34
83    39
84    27
85    36
86    35
87    37
88    33
89    36
90    34
91    36
92    34
93    31
94    29
95    28
96    32
97    34
98    32
99    32
Length: 100, dtype: int64

[PART 2]. 基本Data ETL

2.1 基本運算

In [6]:
df[df.數學==15] #找出數學滿級分的同學
Out[6]:
姓名 國文 英文 數學 自然 社會
0 劉俊安 9 10 15 10 13
24 陳竹伯 10 12 15 10 14
36 詹威德 12 13 15 10 14
37 曾怡君 11 12 15 13 14
39 段冠廷 9 8 15 12 15
59 芮秋辛 11 10 15 10 8
67 林哲法 14 9 15 10 14
70 葉儀依 9 13 15 8 14
73 吳志遠 13 15 15 8 8
76 周育霖 9 12 15 13 12
83 李士賢 10 14 15 13 13
85 張雅彬 10 11 15 12 8
90 胡勝傑 8 11 15 10 15
In [7]:
df[(df.數學==15) & (df.英文==15)] #找出數學和英文都滿級分的同學。要注意 and 要用 &, or 要用 |。每個條件一定要加弧號
Out[7]:
姓名 國文 英文 數學 自然 社會
73 吳志遠 13 15 15 8 8
In [8]:
df[(df.數學==15) | (df.英文==15)] #找出數學 或 英文 滿級分的同學。要注意 and 要用 &, or 要用 |。每個條件一定要加弧號
Out[8]:
姓名 國文 英文 數學 自然 社會
0 劉俊安 9 10 15 10 13
2 黃淑婷 13 15 8 11 14
9 郭雅惠 10 15 12 11 9
12 李正偉 11 15 11 14 15
24 陳竹伯 10 12 15 10 14
33 王佳蓉 14 15 9 9 8
36 詹威德 12 13 15 10 14
37 曾怡君 11 12 15 13 14
39 段冠廷 9 8 15 12 15
43 林士茜 12 15 9 9 13
54 陳怡潔 15 15 9 15 11
55 林意珍 9 15 11 13 9
57 胡淳茜 12 15 14 13 11
59 芮秋辛 11 10 15 10 8
67 林哲法 14 9 15 10 14
70 葉儀依 9 13 15 8 14
73 吳志遠 13 15 15 8 8
76 周育霖 9 12 15 13 12
80 施雅鈴 13 15 12 13 13
83 李士賢 10 14 15 13 13
85 張雅彬 10 11 15 12 8
90 胡勝傑 8 11 15 10 15
In [9]:
df["國英數"] = df[["國文", "英文", "數學"]].sum(1) #新增一個欄位
df.head()
Out[9]:
姓名 國文 英文 數學 自然 社會 國英數
0 劉俊安 9 10 15 10 13 34
1 胡玉華 10 10 10 8 9 30
2 黃淑婷 13 15 8 11 14 36
3 陳上紫 10 10 8 9 14 28
4 崔靜成 13 12 14 12 13 39
In [10]:
df["加權"] = df.數學*2 + df.英文
df.head()
Out[10]:
姓名 國文 英文 數學 自然 社會 國英數 加權
0 劉俊安 9 10 15 10 13 34 40
1 胡玉華 10 10 10 8 9 30 30
2 黃淑婷 13 15 8 11 14 36 31
3 陳上紫 10 10 8 9 14 28 26
4 崔靜成 13 12 14 12 13 39 40
In [11]:
df["總級分"] = df[["國文", "英文", "數學", "社會", "自然"]].sum(1)
df.head()
Out[11]:
姓名 國文 英文 數學 自然 社會 國英數 加權 總級分
0 劉俊安 9 10 15 10 13 34 40 57
1 胡玉華 10 10 10 8 9 30 30 47
2 黃淑婷 13 15 8 11 14 36 31 61
3 陳上紫 10 10 8 9 14 28 26 51
4 崔靜成 13 12 14 12 13 39 40 64

2.2 排序

In [12]:
df.sort_values(by=["總級分"], ascending=False).head(20) #由大排到小
Out[12]:
姓名 國文 英文 數學 自然 社會 國英數 加權 總級分
80 施雅鈴 13 15 12 13 13 40 39 66
12 李正偉 11 15 11 14 15 37 37 66
54 陳怡潔 15 15 9 15 11 39 33 65
25 蔡亦瑄 13 13 14 13 12 40 41 65
57 胡淳茜 12 15 14 13 11 41 43 65
37 曾怡君 11 12 15 13 14 38 42 65
48 陳怡婷 15 14 12 9 15 41 38 65
64 俞志峰 9 14 13 14 15 36 40 65
83 李士賢 10 14 15 13 13 39 44 65
87 趙偉希 10 13 14 13 15 37 41 65
4 崔靜成 13 12 14 12 13 39 40 64
36 詹威德 12 13 15 10 14 40 43 64
11 周皇樺 13 9 13 14 15 35 35 64
68 劉麗芬 8 14 14 14 14 36 42 64
67 林哲法 14 9 15 10 14 38 39 62
20 陳智超 13 9 13 14 13 35 35 62
24 陳竹伯 10 12 15 10 14 37 42 61
65 謝雅琳 13 14 13 12 9 40 40 61
76 周育霖 9 12 15 13 12 36 42 61
44 童心怡 13 13 14 8 13 40 41 61
In [13]:
df.sort_values(by=["加權", "總級分"], ascending=False).head(20) #先比"加權"再比"總級分"
Out[13]:
姓名 國文 英文 數學 自然 社會 國英數 加權 總級分
73 吳志遠 13 15 15 8 8 43 45 59
83 李士賢 10 14 15 13 13 39 44 65
57 胡淳茜 12 15 14 13 11 41 43 65
36 詹威德 12 13 15 10 14 40 43 64
70 葉儀依 9 13 15 8 14 37 43 59
37 曾怡君 11 12 15 13 14 38 42 65
68 劉麗芬 8 14 14 14 14 36 42 64
24 陳竹伯 10 12 15 10 14 37 42 61
76 周育霖 9 12 15 13 12 36 42 61
66 邱盈俊 10 14 14 13 8 38 42 59
25 蔡亦瑄 13 13 14 13 12 40 41 65
87 趙偉希 10 13 14 13 15 37 41 65
44 童心怡 13 13 14 8 13 40 41 61
90 胡勝傑 8 11 15 10 15 34 41 59
85 張雅彬 10 11 15 12 8 36 41 56
27 馮白屏 8 13 14 8 10 35 41 53
64 俞志峰 9 14 13 14 15 36 40 65
4 崔靜成 13 12 14 12 13 39 40 64
65 謝雅琳 13 14 13 12 9 40 40 61
0 劉俊安 9 10 15 10 13 34 40 57

2.3 刪除某一欄或列

In [14]:
df.drop("總級分", axis=1).head(10) #刪掉總級分的那行。
Out[14]:
姓名 國文 英文 數學 自然 社會 國英數 加權
0 劉俊安 9 10 15 10 13 34 40
1 胡玉華 10 10 10 8 9 30 30
2 黃淑婷 13 15 8 11 14 36 31
3 陳上紫 10 10 8 9 14 28 26
4 崔靜成 13 12 14 12 13 39 40
5 張雅岳 13 12 12 12 8 37 36
6 梁俊翔 11 13 10 10 14 34 33
7 林金鳳 8 9 10 10 8 27 29
8 許協旺 9 9 12 10 10 30 33
9 郭雅惠 10 15 12 11 9 37 39
In [15]:
df.drop(5).head(10) #刪掉列就是指定要刪去的 index。
Out[15]:
姓名 國文 英文 數學 自然 社會 國英數 加權 總級分
0 劉俊安 9 10 15 10 13 34 40 57
1 胡玉華 10 10 10 8 9 30 30 47
2 黃淑婷 13 15 8 11 14 36 31 61
3 陳上紫 10 10 8 9 14 28 26 51
4 崔靜成 13 12 14 12 13 39 40 64
6 梁俊翔 11 13 10 10 14 34 33 58
7 林金鳳 8 9 10 10 8 27 29 45
8 許協旺 9 9 12 10 10 30 33 50
9 郭雅惠 10 15 12 11 9 37 39 57
10 張志偉 14 9 8 10 12 31 25 53
In [16]:
df.drop(df[df.姓名=="李士賢"].index).head(10) #或是你也可以這樣寫
Out[16]:
姓名 國文 英文 數學 自然 社會 國英數 加權 總級分
0 劉俊安 9 10 15 10 13 34 40 57
1 胡玉華 10 10 10 8 9 30 30 47
2 黃淑婷 13 15 8 11 14 36 31 61
3 陳上紫 10 10 8 9 14 28 26 51
4 崔靜成 13 12 14 12 13 39 40 64
5 張雅岳 13 12 12 12 8 37 36 57
6 梁俊翔 11 13 10 10 14 34 33 58
7 林金鳳 8 9 10 10 8 27 29 45
8 許協旺 9 9 12 10 10 30 33 50
9 郭雅惠 10 15 12 11 9 37 39 57
In [17]:
#同理可證,我如果只想看特定學生的分數呢
df[df.姓名=="林金鳳"]
Out[17]:
姓名 國文 英文 數學 自然 社會 國英數 加權 總級分
7 林金鳳 8 9 10 10 8 27 29 45

[PART 3]. 基本Data ETL Ⅱ

在本小節,我們要討論的是資料合併 (Pandas Merging)。但是在Pandas中,有關資料合併共有三個函數: Concat 函數、Merge 函數和 Join 函數

Concat 函數可以在下方或旁邊合併一個或多個 dataframe(取決於如何定義軸)

Concat 函數

Merge 函數在作爲主鍵的指定公共列上合併多個 dataframe

Merge 函數

Join 函數合併兩個 dataframe 的方法與 merge 函數類似。但是,它根據索引合併 dataframe,而不是某些指定列。

Join 函數

在本小節,我們僅介紹Merge函數

In [18]:
#老規矩,起手式
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
In [19]:
#載入資料
user_usage = pd.read_csv("user_usage.csv") # 240位手機使用者的使用習慣
user_device = pd.read_csv("user_device.csv")# 272位使用者的手機品牌與系統
devices = pd.read_csv("android_devices.csv")# 14546筆Android整體手機的品牌

print("user_usage dimensions: {}".format(user_usage.shape))
print("user_device dimensions: {}".format(user_device.shape))
print("devices dimensions: {}".format(devices.shape))
user_usage dimensions: (240, 4)
user_device dimensions: (272, 6)
devices dimensions: (14546, 4)
In [20]:
user_usage.head() #手機使用者的使用習慣
Out[20]:
outgoing_mins_per_month outgoing_sms_per_month monthly_mb use_id
0 21.97 4.82 1557.33 22787
1 1710.08 136.88 7267.55 22788
2 1710.08 136.88 7267.55 22789
3 94.46 35.17 519.12 22790
4 71.59 79.26 1557.33 22792
In [21]:
user_device.head()
Out[21]:
use_id user_id platform platform_version device use_type_id
0 22782 26980 ios 10.2 iPhone7,2 2
1 22783 29628 android 6.0 Nexus 5 3
2 22784 28473 android 5.1 SM-G903F 1
3 22785 15200 ios 10.2 iPhone7,2 3
4 22786 28239 android 6.0 ONE E1003 1
In [22]:
devices.head(10)
Out[22]:
Retail Branding Marketing Name Device Model
0 NaN NaN AD681H Smartfren Andromax AD681H
1 NaN NaN FJL21 FJL21
2 NaN NaN T31 Panasonic T31
3 NaN NaN hws7721g MediaPad 7 Youth 2
4 3Q OC1020A OC1020A OC1020A
5 7Eleven IN265 IN265 IN265
6 A.O.I. ELECTRONICS FACTORY A.O.I. TR10CS1_11 TR10CS1
7 AG Mobile AG BOOST 2 BOOST2 E4010
8 AG Mobile AG Flair AG_Flair Flair
9 AG Mobile AG Go Tab Access 2 AG_Go_Tab_Access_2 AG_Go_Tab_Access_2

[3.1] 第一次Merge

merge的參數

on:列名,join用來對齊的那一列的名字,用到這個參數的時候一定要保證左表和右表用來對齊的那一列都有相同的列名。

left_on:左表對齊的列,可以是列名,也可以是和dataframe同樣長度的arrays。

right_on:右表對齊的列,可以是列名,也可以是和dataframe同樣長度的arrays。

left_index/ right_index: 如果是True的haunted以index作為對齊的key

how:數據融合的方法。

sort:根據dataframe合併的keys按字典順序排序,默認是,如果置false可以提高表現。

[merge的默認合併方法]:

- merge用於表內部基於 index-on-index 和 index-on-column(s) 的合併,但默認是基於index來合併。
- pandas 套件的 merge() 方法預設是inner join,如果我們希望使用不同的合併方式,我們可以在 how =參數指定為 left、right 或 outer

DataFrame 的結構

將 user_usage 及 user_device 兩個表格合併

In [23]:
result = pd.merge(user_usage,
                 user_device[['use_id', 'platform', 'device']],
                 on='use_id')
result.head(10)
Out[23]:
outgoing_mins_per_month outgoing_sms_per_month monthly_mb use_id platform device
0 21.97 4.82 1557.33 22787 android GT-I9505
1 1710.08 136.88 7267.55 22788 android SM-G930F
2 1710.08 136.88 7267.55 22789 android SM-G930F
3 94.46 35.17 519.12 22790 android D2303
4 71.59 79.26 1557.33 22792 android SM-G361F
5 71.59 79.26 1557.33 22793 android SM-G361F
6 71.59 79.26 519.12 22794 android SM-G361F
7 71.59 79.26 519.12 22795 android SM-G361F
8 30.92 22.77 3114.67 22799 android ONEPLUS A3003
9 69.80 14.70 25955.55 22801 android GT-I9505
In [24]:
#我們來看看合併之後,剩下多少 dimensions
print("result dimensions: {}".format(result.shape))
result dimensions: (159, 6)
In [25]:
#成功合併及失敗合併各多少筆
user_usage['use_id'].isin(user_device['use_id']).value_counts()
Out[25]:
True     159
False     81
Name: use_id, dtype: int64

[3.2] Left merge

  • 使用左邊data frame的key當作合併的依據
  • pd.merge(left,right,how='left',on=['key1','key2'])
In [28]:
result = pd.merge(user_usage,
                 user_device[['use_id', 'platform', 'device']],
                 on='use_id', how='left')
In [29]:
print("user_usage dimensions: {}".format(user_usage.shape))
print("result dimensions: {}".format(result.shape))
print("There are {} missing values in the result.".format(
        result['device'].isnull().sum()))
user_usage dimensions: (240, 4)
result dimensions: (240, 6)
There are 81 missing values in the result.
In [30]:
result.head(10)
Out[30]:
outgoing_mins_per_month outgoing_sms_per_month monthly_mb use_id platform device
0 21.97 4.82 1557.33 22787 android GT-I9505
1 1710.08 136.88 7267.55 22788 android SM-G930F
2 1710.08 136.88 7267.55 22789 android SM-G930F
3 94.46 35.17 519.12 22790 android D2303
4 71.59 79.26 1557.33 22792 android SM-G361F
5 71.59 79.26 1557.33 22793 android SM-G361F
6 71.59 79.26 519.12 22794 android SM-G361F
7 71.59 79.26 519.12 22795 android SM-G361F
8 30.92 22.77 3114.67 22799 android ONEPLUS A3003
9 69.80 14.70 25955.55 22801 android GT-I9505

[3.3] Right merge

  • 使用右邊data frame的key當作合併的依據
  • pd.merge(left,right,how='right',on=['key1','key2'])
In [31]:
result = pd.merge(user_usage,
                 user_device[['use_id', 'platform', 'device']],
                 on='use_id', how='right')
In [32]:
print("user_device dimensions: {}".format(user_device.shape))
print("result dimensions: {}".format(result.shape))
print("There are {} missing values in the 'monthly_mb' column in the result.".format(
        result['monthly_mb'].isnull().sum()))
print("There are {} missing values in the 'platform' column in the result.".format(
        result['platform'].isnull().sum()))
user_device dimensions: (272, 6)
result dimensions: (272, 6)
There are 113 missing values in the 'monthly_mb' column in the result.
There are 0 missing values in the 'platform' column in the result.
In [33]:
result.head()
Out[33]:
outgoing_mins_per_month outgoing_sms_per_month monthly_mb use_id platform device
0 21.97 4.82 1557.33 22787 android GT-I9505
1 1710.08 136.88 7267.55 22788 android SM-G930F
2 1710.08 136.88 7267.55 22789 android SM-G930F
3 94.46 35.17 519.12 22790 android D2303
4 71.59 79.26 1557.33 22792 android SM-G361F

[3.3] Outer merge

  • 使用相同的key當作合併的依據
  • pd.merge(left,right,how='outer',on=['key1','key2'])
In [34]:
result = pd.merge(user_usage,
                 user_device[['use_id', 'platform', 'device']],
                 on='use_id', how='outer', indicator=True)
In [35]:
print("Outer merge result has {} rows.".format(result.shape))

print("There are {} rows with no missing values.".format(
    (result.apply(lambda x: x.isnull().sum(), axis=1) == 0).sum()))
Outer merge result has (353, 7) rows.
There are 159 rows with no missing values.
In [36]:
result.iloc[[0, 1, 200,201, 350,351]]
Out[36]:
outgoing_mins_per_month outgoing_sms_per_month monthly_mb use_id platform device _merge
0 21.97 4.82 1557.33 22787 android GT-I9505 both
1 1710.08 136.88 7267.55 22788 android SM-G930F both
200 28.79 29.42 3114.67 23988 NaN NaN left_only
201 616.56 99.85 5414.14 24006 NaN NaN left_only
350 NaN NaN NaN 23050 ios iPhone7,2 right_only
351 NaN NaN NaN 23051 ios iPhone7,2 right_only

[3.4] Final Merge : adding device manufacturer

In [37]:
# First, add the platform and device to the user usage.
result = pd.merge(user_usage,
                 user_device[['use_id', 'platform', 'device']],
                 on='use_id',
                 how='left')
In [38]:
# Now, based on the "device" column in result, match the "Model" column in devices.
devices.rename(columns={"Retail Branding": "manufacturer"}, inplace=True)
result = pd.merge(result, 
                  devices[['manufacturer', 'Model']],
                  left_on='device',
                  right_on='Model',
                  how='left')
In [39]:
result.head()
Out[39]:
outgoing_mins_per_month outgoing_sms_per_month monthly_mb use_id platform device manufacturer Model
0 21.97 4.82 1557.33 22787 android GT-I9505 Samsung GT-I9505
1 1710.08 136.88 7267.55 22788 android SM-G930F Samsung SM-G930F
2 1710.08 136.88 7267.55 22789 android SM-G930F Samsung SM-G930F
3 94.46 35.17 519.12 22790 android D2303 Sony D2303
4 71.59 79.26 1557.33 22792 android SM-G361F Samsung SM-G361F

Calculating statistics on final result

In [40]:
result.groupby("manufacturer").agg({
        "outgoing_mins_per_month": "mean",
        "outgoing_sms_per_month": "mean",
        "monthly_mb": "mean",
        "use_id": "count"
    })
Out[40]:
outgoing_mins_per_month outgoing_sms_per_month monthly_mb use_id
manufacturer
HTC 299.842955 93.059318 5144.077955 44
Huawei 81.526667 9.500000 1561.226667 3
LGE 111.530000 12.760000 1557.330000 2
Lava 60.650000 261.900000 12458.670000 2
Lenovo 215.920000 12.930000 1557.330000 2
Motorola 95.127500 65.666250 3946.500000 16
OnePlus 354.855000 48.330000 6575.410000 6
Samsung 191.010093 92.390463 4017.318889 108
Sony 177.315625 40.176250 3212.000625 16
Vodafone 42.750000 46.830000 5191.120000 1
ZTE 42.750000 46.830000 5191.120000 1
In [ ]: