Python 分組處理

cainiao_M發表於2020-12-03

在日常資料分析時,經常會遇到需要按列分組 (groupby) 的任務,如計算某公司各部門的人數,計算各部門男女平均工資,計算不同年代的員工的平均工資等等。在進行這類運算時,Pandas 提供了 groupby 函式,大多數問題它都可以解決,但有一些問題使用 groupby 函式會略顯麻煩,下面我們就這些問題展開細緻的討論。

groupby 是 pandas 中非常重要的一個函式, 主要用於資料分類和聚合計算. 其思想是“split-apply-combine”(拆分 - 應用 - 合併),如下圖:

..

分組原理圖

一、單列分組聚合

單列分組聚合是指把某一列作為鍵進行分組,然後對各組進行聚合運算。

它是上述分組原理的最簡單應用,比如根據員工資訊資料,計算各部門員工數。

問題分析:要計算各部門員工數,首先把部門作為鍵進行分組,然後對各組成員進行計數。

部分員工資訊資料如下:

EID 1 NAME 3 SURNAME GENDER STATE BIRTHDAY HIREDATE DEPT SALARY
1 Rebecca Moore F California 1974/11/20 2005/3/11 R&D 7000
2 Ashley Wilson F New York 1980/7/19 2008/3/16 Finance 11000
3 Rachel Johnson F New Mexico 1970/12/17 2010/12/1 Sales 9000
4 Emily Smith F Texas 1985/3/7 2006/8/15 HR 7000
5 Ashley Smith F Texas 1975/5/13 2004/7/30 R&D 16000

Python程式碼

import pandas as pd
employee = pd.read_csv("Employees.csv")  //讀取資料
dept_emp_num = employee.groupby('DEPT')['DEPT'].count() //分組計數
print(dept_emp_num) 

討論:groupby(‘DEPT’) 將資料按照部門分組, count() 函式進行計數。

二、多列分組聚合

多列分組聚合是指把多列的值同時作為鍵進行分組,然後對各組進行聚合運算。

它和單列分組聚合類似,只是分組的鍵是多列組合而已。如根據員工資訊資料,計算各部門男女員工的平均工資。

繼續使用上例中的員工資訊資料

問題分析:需要分組的鍵有兩個,分別是部門和性別,只要把他們組合起來看作是一個鍵,然後當做單列分組聚合即可。

Python 程式碼

import pandas as pdemployee = pd.read_csv("Employees.csv")
dept_gender_salary =   employee.groupby(['DEPT','GENDER'],as_index=False).SALARY.mean() //多列分組再聚合
print(dept_gender_salary) 

討論:groupby([‘DEPT’,’GENDER’]),分組的兩列以列表的形式作為引數,as_index 表示是否把分組列作為索引,True 表示作為索引,這裡使用 False 表示不作為索引。使用 mean() 函式計算工資的平均值。

三、根據衍生列分組聚合

根據衍生列分組聚合,是指需要分組的鍵並不直接在資料中,需要根據資料計算出一列新資料,把它作為鍵對資料進行分組。如計算不同年代的員工的平均工資。

問題分析:員工資訊資料中並沒有年代這一列,因此需要根據員工的生日列計算出來,把它作為鍵對員工資料進行分組,然後再求工資均值。

Python 程式碼

| import pandas as pd
import numpy as np
employee = pd.read_csv("Employees.csv")
employee['BIRTHDAY']=pd.to_datetime(employee['BIRTHDAY']) //生日列轉換成日期格式
years_salary =   employee.groupby(np.floor((employee['BIRTHDAY'].dt.year-1900)/10)).SALARY.mean() //計算衍生陣列並按此陣列分組,再計算平均工資
print(years_salary) 

討論:年代資料在原資料中並不存在,使用 np.floor((employee[‘BIRTHDAY’].dt.year-1900)/10) 計算出衍生列表示年代,然後根據他分組並計算平均工資。

四、多個聚合

多個聚合,是指分組後對單列或者多列進行多種聚合。

(一) 多列單聚合

多列單聚合,指同時對多列聚合,但每列使用一種聚合方式。如:同時計算各部門員工的人數,平均工資。

問題分析:求員工人數可以對 EID 計數,求平均工資需要對工資列求均值,兩列聚合但每列只用一種聚合方式。

Python 程式碼

import pandas as pd
employee = pd.read_csv("Employees.csv")
dept_agg =   employee.groupby('DEPT',as_index=False).agg({'EID':'count','SALARY':'mean'}) //分組並對 EID 計數,對 SALARY 求平均
print(dept_agg.rename(columns={'EID':'NUM','SALARY':'AVG_SALARY'})) // 重新命名列名 

討論:Pandas 的 agg()函式可以完成這類任務,各列以及各列的聚合方式以字典的形式作為引數傳入 agg(),聚合的列作為字典的鍵,聚合方式作為字典的值,從而完成聚合運算。

(二) 單列多聚合

單列多聚合,指只對一列聚合,但聚合的方式有多種。如上述問題也可以直接對工資計數並求平均,此時是對工資進行了兩種聚合——計數和平均。

Python 程式碼

import pandas as pd
employee = pd.read_csv("Employees.csv")
dept_agg = employee.groupby('DEPT').SALARY.agg(['count','mean']).reset_index() //對 SALARY 計數並求平均
print(dept_agg.rename(columns={'count':'NUM','mean':'AVG_SALARY'})) //重新命名列名

討論:如果是單列的不同聚合方式,則可以把聚合方式進行組合以列表的形式作為引數傳入 agg()。

(三) 多列多聚合

多列多聚合,指對多列聚合同時也包含單列多聚合的組合聚合方式。聚合方式還可以是自己定義的函式,

如:計算各部門員工人數,平均工資和最大年齡。

問題分析:計算員工人數和平均工資,是對工資列計數並求平均(單列多聚合),求最大年齡,需對生日列使用自定義的函式計算出最大年齡。

Python 程式碼

import pandas as pd
import datetime
def max_age(s):    //函式:求最大年齡
    today = datetime. datetime.today().year     //年份
    age = today-s.dt.year    //求年齡
    return age.max()
employee = pd.read_csv("Employees.csv")
employee['BIRTHDAY']=pd.to_datetime(employee['BIRTHDAY'])
dept_agg =   employee.groupby('DEPT').agg({'SALARY':['count','mean'],'BIRTHDAY':max_age}) //按 DEPT 分組,根據 SALARY 計數和求均值,BIRTHDAY 使用 max_age 計算最大年齡
dept_agg.columns   = ['NUM','AVG_SALARY','MAX_AGE'] //修改列名
print(dept_agg.reset_index()) 

討論:這種情況,聚合列和聚合方式還是按照字典的方式傳入,但當某一列需要多種聚合方式時,則需要將其組合,以列表的形式作為字典的值。

五、分組聚合值複製

分組聚合值複製,指把分組聚合的結果轉換成與該組等長的列,相當於把聚合的結果複製到該組的所有行。如:為員工資訊資料新增一列各部門的平均工資。

問題分析:各部門的平均工資需要按照部門分組再對工資求平均,把平均工資的值新增到對應的組,並保持資料原序。

Python 程式碼

import pandas as pd
employee = pd.read_csv("Employees.csv")
employee['AVG_SALARY'] =   employee.groupby('DEPT').SALARY.transform('mean') //按照 DEPT 分組並對 SALARY 求平均
print(employee) 

討論:按照部門分組後,對工資求均值。transform() 函式在組內求聚合值後會按照原索引的順序返回結果,可以自動按照索引新增結果,從而保證原資料順序不變。

六、分組子集處理

分組應用:指分組後對各組進行一些非聚合運算。比如分組排序,分組後不再關心聚合的結果,而是關心組內記錄的順序。如:將各部門按照入職時間從早到晚進行排序 。

問題分析:按照部門分組後,不再關心分組後的聚合結果,而是關心員工的入職時間順序。分組後,對各組進行迴圈同時對組內成員按照入職時間排序就可以了。

Python 程式碼

import pandas as pdemployee = pd.read_csv("Employees.csv")
employee['HIREDATE']=pd.to_datetime(employee['HIREDATE']) //修改入職時間格式
employee_new =   employee.groupby('DEPT',as_index=False).apply(lambda x:x.sort_values('HIREDATE')).reset_index(drop=True)//按 DEPT 分組,並對各組按照 HIREDATE 排序,最後重置索引 
print(employee_new) 

討論:分組後需要對組內成員排序,可以使用 apply()函式結合 lambda 的方式,其中 lambda 表示式是對各組迴圈,使用 sort_values() 函式在組內部再排序,返回組內排序的結果。

簡單的運算使用 lambda 函式計算,但有時會遇到比較複雜的計算,如:計算各部門年齡最大的員工和年齡最小的員工的工資差。

問題分析:首先需按照部門分組,分組後還需要找到年齡最大的員工和年齡最小的員工的記錄,然後才能計算工資差。

Python 程式碼

import pandas as pd
def salary_diff(g):     //函式:計算各組工資差
    max_age =   g['BIRTHDAY'].idxmin()    //年齡最大的索引
    min_age =   g['BIRTHDAY'].idxmax()    //年齡最小的索引
    diff =   g.loc[max_age]['SALARY']-g.loc[min_age]['SALARY']            return diff
employee = pd.read_csv("Employees.csv")
employee['BIRTHDAY']=pd.to_datetime(employee['BIRTHDAY'])//計算工資差按 DEPT 分組並使用自定義函式計算
salary_diff = employee.groupby('DEPT').apply(salary_diff)
print(salary_diff) 

討論:使用 apply()結合自定義函式的方式。其中 apply() 會把分組的結果作為引數傳入自定義函式。salary_diff() 函式是自定義函式,g 實質上就是 pandas 的 DataFrame 格式的資料框,這裡是分組的結果。對它計算最大年齡和最小年齡的索引後,找到工資欄位計算差即得到結果。

思考:

由上述討論可見,熟練掌握 Pandas 的這些 groupby 方法對我們進行資料分析是特別有幫助的。

下面我們以 stack overflow 網站上的一些實際問題來進一步瞭解 groupby。

七、按位置分組

按位置分組,指不以某列作為鍵分組,而是以記錄的位置作為鍵來分組。比如將資料每三行分到相同組或者按照位置分成奇數位置一組,偶數位置一組等。舉例如下:

source: https://stackoverflow.com/questions/59110612/pandas-groupby-mode-every-n-rows

資料片段如下:

time a b

0 0.5 -2.0

1 0.5 -2.0

2 0.1 -1.0

3 0.1 -1.0

4 0.1 -1.0

5 0.5 -1.0

6 0.5 -1.0

7 0.5 -3.0

8 0.5 -1.0

希望每三行分成一組,並把眾數作為該組的結果。理想的結果如下:

time a b

2 0.5 -2.0

5 0.1 -1.0

8 0.5 -1.0

問題分析:該問題的分組與現有的列沒有關係,只與位置相關,因此需要衍生出一列作為分組依據,按位置做整數乘法即得到衍生列,然後據此分組即可。

Python 程式碼

import pandas as pd
import numpy as np
data = pd.read_csv("group3.txt",sep='\t')
res = data.groupby(np.arange(len(data))  \//   3).agg(lambda x: x.mode().iloc[-1]) //按照衍生列分組,使用 agg 結合 lambda 的方式得到眾數,取各組各列的最後 1 個眾數作為結果
print(res) 

討論:衍生列計算方式為 np.arange(len(data)) // 3,其結果是 [0 0 0 1 1 1 2 2 2],把它作為鍵進行分組就可以把資料分成每三行一組。而 agg(lambda x: x.mode()) 則是將各組的各列分別求眾數,如第一組 time 的眾數為 [0,1,2] 而 a 和 b 的眾數分別是 [0.5] 和[-2.0]分別取最後 1 個眾數 iloc[-1]即得到想要的結果。

八、值變化分組

值變化分組,指在有序的資料中,發生資料變化時就分出一個新組。舉例如下:

source: https://stackoverflow.com/questions/41620920/groupby-conditional-sum-of-adjacent-rows-pandas

資料片段如下:

  duration  location  user

0 10 house A

1 5 house A

2 5 gym A

3 4 gym B

4 10 shop B

5 4 gym B

6 6 gym B

按照 user 分組後,各組當 location 連續相同時對 duration 進行求和,location 變化時則重新求和。理想結果如下:

duration location user

    15    house    A

     5      gym    A

     4      gym    B

    10     shop    B

    10      gym    B

問題分析:location 列的順序很重要,連續相同時可以視為一組,當變化時則重新分一組,如 user=B 時,第 4 行 (索引為 3) 的 location 為 [gym,shop,gym,gym], 不可以把其中的 3 個 gym 分到 1 組,而應該把第一個 gym 單獨作為 1 組,shop 與 gym 不同,值發生了變化,把 shop 分到下一組,後面兩個 gym 沒有值變化,可以分到同一組,分組的結果為[[gym],[shop],[gym,gym]],所以這裡不可以使用 df.groupby([‘user’,’location’]).duration.sum() 來計算結果,而是要想辦法生成一個衍生列作為分組依據。

程式碼如下:

import pandas as pd
//生成資料
df = pd.DataFrame({'user' : ['A', 'A', 'A', 'B', 'B',   'B','B'],                'location' : ['house','house','gym','gym','shop','gym','gym'],                'duration':[10,5,5,4,10,4,6]})
derive = (df.location !=   df.location.shift()).cumsum() //創造衍生列
res = df.groupby(['user', 'location', derive],   as_index=False, sort=False)['duration'].sum() //按照 user,location 和衍生列分組,對 duraton 求和
print(res) 

討論:衍生列 derive 是當 location 與前者不同時進行累加,得到 [1 1 2 2 3 4 4]。然後按照 user,location 和該數列分組,再對 duration 求和。

九、條件變化分組

條件變化分組:指在有序的資料中,當滿足某一條件時重新分組。舉例如下:

source: https://stackoverflow.com/questions/62461647/choose-random-rows-in-pandas-datafram

資料片段如下:

ID code

333_c_132 x

333_c_132 n06

333_c_132 n36

333_c_132 n60

333_c_132 n72

333_c_132 n84

333_c_132 n96

333_c_132 n108

333_c_132 n120

999_c_133 x

999_c_133 n06

999_c_133 n12

999_c_133 n24

998_c_134 x

998_c_134 n06

998_c_134 n12

998_c_134 n18

998_c_134 n36

997_c_135 x

997_c_135 n06

997_c_135 n12

997_c_135 n24

997_c_135 n36

996_c_136 x

996_c_136 n06

996_c_136 n12

996_c_136 n18

996_c_136 n24

996_c_136 n36

995_c_137 x

希望從 code 列的每兩個 x 中間隨機取一行

理想結果形式如下:

333_c_132 n06

999_c_133 n12

998_c_134 n18

997_c_135 n36

996_c_136 n18

問題分析:取兩個 x 之間的隨機一條記錄,可以轉化成每當 code 等於 x 時開始新的一組,不等於 x 時分組不變,然後從該組中隨機取一行。因此這裡還是需要生成衍生列,把它作為鍵分組才能完成任務。

程式碼如下:

import pandas as pd
df = pd.read_csv("data.txt")
derive = df.code.eq('x').cumsum() //生成衍生列
res=df[df.code.ne('x')].groupby(derive).apply(lambda   x : x.sample(1)) // 根據衍生列分組,使用 apply 結合 lambda 的方式隨機抽樣
res=res.reset_index(level=0, drop=True)//重置索引
print(res)  

討論:code.eq(x) 表示 code 等於 x 時為 True,其餘為 False,cumsum()表示對其累加,生成的衍生列為 [1 1 1 1 1 1 1 1 1 2 2…],過濾掉等於 x 的列再根據該列進行分組並抽樣即可。

思考:

前面所有的例子都是將原集合根據某個條件,將資料劃分成若干個子集,且滿足以下兩點:

1)沒有空子集

2)原集合的任何成員都屬於且只屬於某一個子集

我們稱這種劃分方式為完全劃分。那麼有沒有不完全劃分呢?

來看下面這幾個例子

十、對位分組

對位分組,指先羅列出一個基準集合,然後將待分組集合成員的某個屬性(欄位或表示式)與基準集合成員比較,相同者則分到一個子集中,最後拆分出來的子集數量和基準集合成員數是相同的。對位分組有三個特點:

1)可能出現空子集(比如基準集合的某些成員在待分組集合中並不存在);

2)可能有待分組集合成員未被分到任何子集(比如有些不重要的成員未被列入基準集合);

3)每個成員最多隻出現在一個子集中。

(一)出現空子集

公司統計各部門男女人數,如果某個部門沒有男員工或者沒有女員工,則將該部門的男員工人數或女員工人數填為 0。

問題分析:如果直接按照部門和性別分組,則如果某個部門沒有女員工或沒有男員工時,該部門將只被分成 1 組,就會丟失掉缺少的性別的統計資訊,因此不可以直接 groupby([‘DEPT’,’GENDER’])。很容易想到的方案就是,先按部門分組,羅列出 [男, 女] 的基準集合,使用左連線 (left join) 的方式與各組連線,再對連線後的結果按照性別分組,最後彙總結果,這樣就能保證分組的結果總會有 [男, 女] 了。

Python 程式碼

import pandas as pd
def align_group(g,l,by):     //函式,對位分組
    d = pd.DataFrame(l,columns=[by])    //生成對照的 dataframe
    m =   pd.merge(d,g,on=by,how='left')//利用 merge 完成對位運算
return m.groupby(by,sort=False)//分組
employee = pd.read_csv("Employees.csv")
l = ['M','F']//指定序列
res = employee.groupby('DEPT').apply(lambda   x:align_group(x, l, 'GENDER').apply(lambda s:s.EID.count()))//按 DEPT 分組,再對各組使用函式對位分組,對 EID 進行計數
print(res) 

討論:

自定義函式 align_group,使用 merge()函式完成羅列集合與待分組集合的 left join,再按 merge 的列進行分組。按部門分組後,使用 apply() 結合 lambda 表示式的方式對每組使用自定義函式對位分組,最後對 EID 列計數得到最終結果。(注意:這裡不可以對 GENDER 計數,因為 merge 時 GENDER 的成員都被保留了,如果有空子集時,對它計數結果將是 1,而其他列(比如 EID), 在 left join 時會是空值,所以對 EID 計數結果是 0)。

(二)有待分組集合成員未被分到任何子集

按指定的部門 [‘Administration’, ‘HR’, ‘Marketing’, ‘Sales’] 分組,只查詢這幾個部門的人數且部門先後順序保持不變。

問題分析:與出現空子集的情況類似,此時也可以使用 left join 的方式,將不在預先羅列的集合成員排除掉,只保留羅列集合中的成員。

程式碼如下:

import pandas as pd
def align_group(g,l,by):   //函式,對位分組
    d =   pd.DataFrame(l,columns=[by])    
    m =   pd.merge(d,g,on=by,how='left')    
    return   m.groupby(by,sort=False)
employee = pd.read_csv("Employees.csv")
sub_dept = ['Administration', 'HR', 'Marketing',   'Sales'] //指定順序的部門子集
res =   align_group(employee,sub_dept,'DEPT').apply(lambda x:x.EID.count()) //使用對位分組函式分組,再對 EID 計數
print(res) 

討論:Pandas 不直接支援對位分組的功能,因此完成起來成本就會比較高,而且使用 merge 函式也會導致執行效率低下。

十一、列舉分組

列舉分組:事先指定一組條件,將待分組集合的成員作為引數計算這批條件,條件成立者被劃分到與該條件對應的一個子集中,結果集的子集和事先指定的條件一一對應。列舉分組的特點:允許集合成員重複出現在不同的子集中。

舉例如下:

按在公司的工齡將員工分組統計每組的男女員工人數(分組條件重合時,列出所有滿足條件的員工,分組的條件是 [工齡 <5 年,5 年 <= 工齡 <10 年,工齡 >=10 年,工齡 >=15 年])

問題分析:工齡 >=10 年和工齡 >=15 年兩個條件有重複的區間,即工齡大於 15 年的員工,其工齡也一定大於 10 年,這時如果使用構造衍生列的方式來完成,將無法使同一個成員重複出現在兩個分組中,因此需要考慮每個條件都分一次組,然後找出滿足條件的組,最後再彙總。

import pandas as pd
import datetime
def eval_g(dd:dict,ss:str):    //函式,字串轉表示式
    return   eval(ss,dd)   
emp_file = 'E:\\txt\\employee.txt'
emp_info = pd.read_csv(emp_file,sep='\t')
employed_list = ['Within five years','Five to ten   years','More than ten years','Over fifteen years']
employed_str_list =   ["(s<5)","(s>=5) &   (s<10)","(s>=10)","(s>=15)"] //分組條件
today = datetime.datetime.today().year
arr = pd.to_datetime(emp_info['HIREDATE'])
employed = today-arr.dt.year
emp_info['EMPLOYED']=employed  //計算入職時間
dd = {'s':emp_info['EMPLOYED']}
group_cond = []
for n in range(len(employed_str_list)):    //迴圈分組條件
    emp_g = emp_info.groupby(eval_g(dd,employed_str_list[n]))    //按分組條件分組        
    emp_g_index   = [index for index in emp_g.size().index]    //分組索引
    if True not   in emp_g_index:          //如果沒有滿足條件的成員
        female_emp=0          //男女員工數為 0
        male_emp=0    
    else:        //滿足條件
        group =   emp_g.get_group(True)        //獲取分組
        sum_emp   = len(group)          //計算男女員工人數
            female_emp = len(group[group['GENDER']=='F'])                          male_emp = sum_emp-female_emp      group_cond.append([employed_list[n],male_emp,female_emp])//彙總各個分組條件的計算結果
group_df =   pd.DataFrame(group_cond,columns=['EMPLOYED','MALE','FEMALE'])
print(group_df)

討論:EMPLOYED 是根據入職時間 HIREDATE 新增加的一列,表示工齡。自定義函式 eval_g(),是把分組的條件轉換成表示式,比如當條件是 s<5 時,eval_g(dd,ss)的表示式就是 emp_info[‘EMPLOYED’]<5,根據這個衍生列來對資料分組。對分組條件進行迴圈,按該衍生列分成兩組,get_group(True) 表示取滿足條件的組,最後把所有滿足條件的結果使用 concat() 函式彙總。

總結

Python 在進行分組處理時,多數情況可以比較優雅的處理,但在處理有序分組時,如值變化分組、條件變化分組時則需要自己想辦法生成滿足分組條件的衍生列,略顯麻煩。對位分組和列舉分組的兩種情況更是糟糕,需要自己想辦法去繞,要麼使用 merge 運算,要麼多次分組,使分組的成本變得很高,這樣看來,Pandas 的分組運算還有其侷限性。

對於分組運算,相比之下,esProc SPL 處理的更完善。 esProc 是專業的資料計算引擎,SPL 提供了豐富的分組運算,可以方便的完成上述任務,程式碼風格的一致程度也更好。

兩個分組運算函式 groups()和 group(),分別實現分組聚合和分組子集,可以比 Python 更簡潔地解決前面六個常規分組問題:

問題 SPL程式碼 簡單說明
A.groups(DEPT;count(~):NUM) A是資料表,按 DEPT 分組,count() 計數
A.groups(DEPT,GENDER;avg(SALARY):AVG_SALARY) 按 DEPT,GENDER 分組,avg() 平均
A.groups((year(BIRTHDAY)-1900)\10:years;avg(SALARY):AVG_SALARY) (year(BIRTHDAY)-1900)\10命名為 years,並分組
A.groups(DEPT;count(EID):NUM,avg(SALARY):AVG_SALARY) 多列單聚合
A.groups(DEPT;count(SALARY):NUM,avg(SALARY):AVG_SALARY) 單列多聚合
A.groups(DEPT;count(SALARY):NUM,avg(SALARY):AVG_SALARY,max(age(BIRTHDAY)):MAX_AVG) 多列多聚合
B=A.derive(AVG_SALARY)>B.group(DEPT).((a=.avg(SALARY),.run(AVG_SALARY=a))) 增加新列修改為分組的聚合值
A.group(DEPT).conj(~.sort(HIREDATE)) 分組排序
A.group(DEPT;(ma=.minp(BIRTHDAY),mi=.maxp(BIRTHDAY),ma.SALARY-mi.SALARY):SALARY_DIF) 分組子集運算

對於這六個簡單分組計算,Python 的分組計算方法同樣方便。但涉及了很多其他函式,如 agg,transform,apply,lambda 表示式甚至是自定義函式等等,程式碼風格差別比較大。而 SPL 則基本保持了 groups(x;y) 或者是 group(x).(y) 這樣統一的程式碼風格。

對於問題七、八、九,Python 就略顯煩瑣,需想辦法生成衍生列,而 SPL 本身基於有序集合設計,提供了有序分組的選項,仍可以優雅的保持簡單運算時的程式碼風格。

問題 SPL程式碼 簡單說明
A.groups@n((#-1)\3;y) 每三行分 1 組,y 是聚合運算表示式
A.groups@o(user,location;y) @o選項:值變化分組
A.group@i(code==”x”).(y) @i選項:條件變化分組

根據分組後直接聚合還是分組後針對子集計算,靈活選擇 groups 和 group 函式。

最後兩個問題,對位分組和列舉分組,確實有點難為 Python 了,不過不管是使用 merge 函式繞還是多次分組,總算是完成了任務。而 SPL 提供了專門的對位分組函式 align()和列舉分組函式 enum(),可以繼續優雅。

問題 SPL分組處理 簡單說明
s=[“M”,”F”]A.group(DEPT).(~.align@a(s,GENDER).(y)) 可能出現空子集
s=[“Administration”, “HR”, “Marketing”, “Sales”]A.align@a(s,DEPT).(y) 有待分組集合成員未被分到任何子集
十一 c=[“?<5”,”?>=5 && ?<10”,”?>=10”,”?>=15”]A.enum(c, EMPLOYED) 有成員被分到不同子集

需要提到的是,Python 還有一個致命缺點——大資料(無法一次性讀入記憶體)分組,它涉及到外存讀寫和 hash 分組,對於非專業的程式設計師來說,使用 Python 完成這個任務幾乎是不可能的。有興趣可以參考以下文章:

Python 如何處理大檔案

這裡介紹了 Python 處理大資料存在的問題(包括大資料分組),也簡單介紹了 esProc SPL 中的遊標系統,其中 group 和 groupx() 函式仍然可以優雅的完成大資料分組任務。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章