目錄:
****1. Numpy-diag 矩陣變換
- stack()/unstack()
- pd.pivot_table()
- pd.melt()
- groupby聚類算法
- mapping小技巧
-
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í)~~~