pandas/numpy數(shù)據(jù)結(jié)構(gòu)算法(之行列變換)(二) (tag:行列轉(zhuǎn)換,迪卡爾積,內(nèi)置函數(shù),數(shù)據(jù)結(jié)構(gòu))

目錄:

****1. Numpy-diag 矩陣變換

  1. stack()/unstack()
  2. pd.pivot_table()
  3. pd.melt()
  4. groupby聚類算法
  5. mapping小技巧
  6. numpy.vectorize()**


    在這**里插入圖片描述

前言

最近遇到很多需要迭代和歸并數(shù)據(jù)的情況,一直以來的做法,都是循環(huán)主要的鍵,去進行后續(xù)操作。這是最典型的Python 操作,然而還是上次提到的效率問題。記得之前朋友和我講過Py的歷史,甚至反思了下自己的定位。
我們寫好的py腳本,會通過解釋器翻譯成機器可以識別并執(zhí)行的信息。解釋器包括:編譯器和虛擬機(與Java類似)。之前聽朋友講過,如果py腳本自身已經(jīng)優(yōu)化到極致,就可以考慮Cython和Numba來進一步加快速的,如果是大數(shù)據(jù),可以考慮py-spark這種偽分布式,可以通過Modin庫合理分配cpu多核運算。

pip --default-timeout=100 install modin[ray]
pip -i https://pypi.tuna.tsinghua.edu.cn/simple --default-timeout=100 install pyspark

以下就記錄一下最近遇到的一些情況,以及優(yōu)化方式。

1. Numpy-diag 矩陣變換

numpy.diag簡單說就是對角線,線性代數(shù)矩陣?yán)锩鎸蔷€作為一個list。

>>>array_1=np.array([1,0,0,0],[0,2,0,0],[0,0,3,0],[0,0,0,4])
   np.diag(array_1)
out:
[1,2,3,4]
>>>array_2=np.array([2,3,4])
   np.diag(array_2)
out:
array([2,0,0],[0,3,0],[0,0,4])

這種方法放在pandas的dataframe里也可以用。比如,我們有一列數(shù)據(jù),每4個為一組,這樣就可以按照這種方式,進行數(shù)據(jù)結(jié)構(gòu)的轉(zhuǎn)換。不用再去循環(huán)運算。

2. stack()/unstack()

這種方法可以本身是用于數(shù)據(jù)轉(zhuǎn)置,stack()為列轉(zhuǎn)行,unstack()為行傳列,如:

A B C
1 2 3
2 6 4
stack()后變成:
1 2
2 6
3 4

因此,我們可以用于數(shù)據(jù)增維。比如,一般py代碼在增加維度(改變某一個鍵值,復(fù)制第一行數(shù)據(jù)),都會首先copy第一行數(shù)據(jù),進行增加復(fù)制,這樣避免不了遍歷所有需要復(fù)制的對象,因此效率過低。所以我們可以通過這種方法,一步到位。

#普通遍歷方式
>>>df_new=pd.DataFrame(columns=['name','Age','class'])
   for i in range(len(df['Age'])):
       df_temp=df.loc[0,:].copy()
       df_temp['cal']=['A','B','C']
       df_new=df_new.append(df_temp)
>>>print(df_new)

此種方法思路較為傳統(tǒng):1. 對整個dataframe進行遍歷,復(fù)制每一行。2. 將對應(yīng)的List插入這個temp df。3. 最后用之前建好的新的空dataframe df_new去裝載這些新擴充好的df_temp。
也就是加入之前有n行,擴充后變成3*n行數(shù)據(jù)。
此種方法時間復(fù)雜度較高,如果需要擴充的數(shù)據(jù)為萬行以上就會特別慢。上次講到pandas的內(nèi)置函數(shù),他本質(zhì)就是C語言的映射,內(nèi)在的遍歷是直接執(zhí)行C代碼,因此我們一定要有限考慮內(nèi)置函數(shù)。
如上述算法可以改成:

>>>temp_str='A,B,C'
    df['cal']=temp_str
    df_1=df['cal'].str.split(',',expand=True)
    df_1=df_1.stack()
    df_1=df_1.reset_index(level=1, drop=True)
    df_2=pd.DataFrame(df_1,columns=['cal'])
    df_final=df.drop(['cal'], axis=1).join(df_2)
    print(df_final)

如此一來,速度可以快了6倍左右。有幾個小時tip需要注意:

1.join的時候默認匹配原有的index,所以新建的list才會完美銜接原有df,自動復(fù)制Index相同的rows。
2.unstack()可以進行反向操作.

下面附上筆者自己寫的一段代碼,可以改一改,進行增row和增columns的操作:

##添加n列:將period放在列上
class add_row_col():
    def __init__(self, dataframe, new_col_horizontal, new_col_vertical, counts=None):
        self.d = dataframe
        self.h = new_col_horizontal
        self.n = new_col_vertical
        self.c = counts

    # 創(chuàng)建一個p1---p?的字符串,逗號間隔。
    def create_str(self):
        P_list = list()
        for i in range(1, self.c + 1):
            N = str(str(self.h)) + str(i)
            P_list.append(N)
        PStr = ','.join(P_list)
        return PStr

    # 創(chuàng)建P1-P12的list,增加每個FA的維度。(12*N行)
    def create_matrix(self):
        P_list = list()
        for i in range(1, self.c + 1):
            N = str(str(self.h)) + str(i)
            P_list.append(N)
        return [a for a in P_list]

    # 增加維度?。◤?fù)制N行)
    def multi_dimension(self):
        str_list = self.create_str()
        df_1 = self.d
        df_1[str(self.n)] = str(str_list)
        df_1[str(self.n)] = df_1[str(self.n)].apply(lambda y: y.replace('[', '').replace(']', ''))
        df_temp = df_1[str(self.n)].str.split(',', expand=True)
        df_temp = df_temp.stack()
        df_temp = df_temp.reset_index(level=1, drop=True)
        df_temp = pd.DataFrame(df_temp, columns=[str(self.n)])
        df_2 = df_1.drop([str(self.n)], axis=1).join(df_temp)
        df_2[str(self.n)] = df_2[str(self.n)].apply(lambda x: x.replace("'", ""))
        df_2.reset_index(drop=True, inplace=True)
        return df_2

    # 增加維度?。◤?fù)制N列)
    def create_col(self):
        df = self.d.copy()
        matrix = self.create_matrix()
        for g in matrix:
            df[g] = 0
        return df  
        
#引用的時候:
>>>df_2=add_row_col(df_1,'P','Period_list',counts=12).multi_dimension() #縱行增值放在一列
>>>df_2=add_row_col(df_1, 'P', 'Period_list', counts=12).create_col() #橫向增值放在多列

3. pd.pivot_table() (行轉(zhuǎn)列)

這個東西太方便了,和同事都贊不絕口。仿佛就是再用excel的 pivot。自由組合想要的鍵值,給予最直觀的展示。

詳細參數(shù)解釋:pd.pivot_table()

簡單說是行列轉(zhuǎn)換,或者我們想看到例如某個城市,某個年份的某數(shù)據(jù)時,會用到,即多個constraint限制同一個value。
舉個例子看就清楚了:

>>>df_2 = pd.pivot_table(df_1, index='City', columns='Time', values='Birth_Rate')
###city固定在左邊,Time作為橫向的時間軸,中間的值為value,表示某個城市,某一個時間的出生率。
>>>df_2 = pd.pivot_table(df_1, index=['City','District'], columns=['Time','Time2'], values=['Birth_Rate','Death_Rate'])
###當(dāng)然我們的標(biāo)準(zhǔn)也可以變得更多維度,將參數(shù)替換成list也可。

4. pd.melt() (列轉(zhuǎn)行)

剛才提到的pivot_table是行轉(zhuǎn)列,而melt()則是列轉(zhuǎn)行。

詳細參數(shù)解釋:pd.melt()

舉個例子來看:

[In]:A  B  C
     1  2  3
     4  5  6
>>>df_2=pd.melt(df_1, id_vars='A', value_vars='C', var_name='C_name', value_name='Value_name')
[Out]:A  C_name  Value_name
      1   C         3
      4   C         6

5. groupby聚類算法

groupby一般可以用來按照特性聚合某一類數(shù)據(jù)。官方給的解釋很晦澀,

詳細解釋:groupby()

其實簡單理解,就是filter。但是和filter不太一樣,filter是單一的一個步驟,而groupby更像一個放大鏡,我的目的是通過鏡子看到更細致的東西,而不只是打開放大鏡那么簡單。這樣groupby就可以和你任何想進行的后續(xù)操作結(jié)合。先來看一個簡單的filter 和后續(xù)操作:

##先篩選這一類數(shù)據(jù)
df_2=df_1[df_1['A']==20]
##再做計算
df_2['C']=df_2['B'].apply(lambda x: x.mean()) 
"""
但是如果你的數(shù)據(jù)有數(shù)以千萬條,或者你要篩選的條件不只一個,比如可以寫成如下:
"""
df_base_input_1=df_base_input[(df_base_input['Retail_or_Non_Retail']=='RETAIL')
&(~df_base_input['LE'].isin(['740','712','714','738']))
&(df_base_input['DeprnAccount'].isin(['683000.0000','684000.0000']))
&(df_base_input['Cost']!=0)]
"""
此時,速度就會稍微慢一些,因為你把一步可以做完的是拆成多步,且還同時占用了更多的空間。除非篩選這一步需要保存,否則用groupby更佳。
"""
##比如以下操作,同樣是求平均數(shù),一步到位。
>>>df_temp['SUMMPANY']=df_sum.groupby(['ENTITY_CODE','PERIOD_TIME'])['Actual_area'].mean()
##或者如此:
>>>df_temp['SUMMPANY']=df_sum.groupby(['ENTITY_CODE','PERIOD_TIME']).apply(lambda x: x['ACTUAL_AREA'].sum())

6. mapping小技巧

傳統(tǒng)的mapping方法,可以合并多表,在進行多列操作,不過這樣占用空間很大??梢杂玫降暮喜⒎绞胶芏?,比如:

pd.merge()
pd.concat()
join()
append()

但是,這里說的mapping小技巧,是自己發(fā)現(xiàn)的,有不對的地方,歡迎大神指正。

>>>df_map=df_1[df_1['A'].isin['dog','cat','mat','frog']].groupby('age')['B'].mean()
"""
這個df_map便是一個小的查詢表單:
>age     B
>5歲    2.334
>6歲    3.456
>7歲    5.662
>8歲    7.028
之后我們在引用的時候可以直接抓取需要的年齡對應(yīng)的那個平均數(shù).
>此處的df_map[str(x['Age'])]便是我們要抓取的用于計算的平均數(shù)。
>>>df_1['calulation']=df_1.apply(lambda x: x['BB']/df_map[str(x['Age'])],axis=1)

7. numpy.vectorize()

這個矢量化也很好用,專門處理數(shù)組運算,有點類似于pandas里的apply lambda。

詳細解釋:numpy.vectorize()

簡單的用法就是把編輯好的function 放在里面,再去運行。很多時候一個dataframe里運算負荷太大,如果只抽取某一數(shù)組更為方便。如[a,b,c,d,e]與A的運算。

def my_function(a, b):
    count=0
    for i in range(0, 5):
        if count<3:
            a+=i
        else:
            b+=i
        count+=1
>>>np.vectorize(my_function)([1,2,3,4],5)

這個count也是很神奇,leetcode初級題中經(jīng)常出現(xiàn)。

未完待續(xù)......如有錯誤,歡迎大神指正,一起進步學(xué)習(xí)~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容