pandas/numpy数据结构算法(之行列变换)(二) (tag:行列转换,迪卡尔积,内置函数,数据结构)

目录:

****1. Numpy-diag 矩阵变换

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


    在这**里插入图片描述

前言

最近遇到很多需要迭代和归并数据的情况,一直以来的做法,都是循环主要的键,去进行后续操作。这是最典型的Python 操作,然而还是上次提到的效率问题。记得之前朋友和我讲过Py的历史,甚至反思了下自己的定位。
我们写好的py脚本,会通过解释器翻译成机器可以识别并执行的信息。解释器包括:编译器和虚拟机(与Java类似)。之前听朋友讲过,如果py脚本自身已经优化到极致,就可以考虑Cython和Numba来进一步加快速的,如果是大数据,可以考虑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

以下就记录一下最近遇到的一些情况,以及优化方式。

1. Numpy-diag 矩阵变换

numpy.diag简单说就是对角线,线性代数矩阵里面对角线作为一个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里也可以用。比如,我们有一列数据,每4个为一组,这样就可以按照这种方式,进行数据结构的转换。不用再去循环运算。

2. stack()/unstack()

这种方法可以本身是用于数据转置,stack()为列转行,unstack()为行传列,如:

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

因此,我们可以用于数据增维。比如,一般py代码在增加维度(改变某一个键值,复制第一行数据),都会首先copy第一行数据,进行增加复制,这样避免不了遍历所有需要复制的对象,因此效率过低。所以我们可以通过这种方法,一步到位。

#普通遍历方式
>>>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)

此种方法思路较为传统:1. 对整个dataframe进行遍历,复制每一行。2. 将对应的List插入这个temp df。3. 最后用之前建好的新的空dataframe df_new去装载这些新扩充好的df_temp。
也就是加入之前有n行,扩充后变成3*n行数据。
此种方法时间复杂度较高,如果需要扩充的数据为万行以上就会特别慢。上次讲到pandas的内置函数,他本质就是C语言的映射,内在的遍历是直接执行C代码,因此我们一定要有限考虑内置函数。
如上述算法可以改成:

>>>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,自动复制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

    # 创建一个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

    # 创建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]

    # 增加维度!(复制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

    # 增加维度!(复制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() (行转列)

这个东西太方便了,和同事都赞不绝口。仿佛就是再用excel的 pivot。自由组合想要的键值,给予最直观的展示。

详细参数解释:pd.pivot_table()

简单说是行列转换,或者我们想看到例如某个城市,某个年份的某数据时,会用到,即多个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'])
###当然我们的标准也可以变得更多维度,将参数替换成list也可。

4. pd.melt() (列转行)

刚才提到的pivot_table是行转列,而melt()则是列转行。

详细参数解释: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一般可以用来按照特性聚合某一类数据。官方给的解释很晦涩,

详细解释:groupby()

其实简单理解,就是filter。但是和filter不太一样,filter是单一的一个步骤,而groupby更像一个放大镜,我的目的是通过镜子看到更细致的东西,而不只是打开放大镜那么简单。这样groupby就可以和你任何想进行的后续操作结合。先来看一个简单的filter 和后续操作:

##先筛选这一类数据
df_2=df_1[df_1['A']==20]
##再做计算
df_2['C']=df_2['B'].apply(lambda x: x.mean()) 
"""
但是如果你的数据有数以千万条,或者你要筛选的条件不只一个,比如可以写成如下:
"""
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更佳。
"""
##比如以下操作,同样是求平均数,一步到位。
>>>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小技巧

传统的mapping方法,可以合并多表,在进行多列操作,不过这样占用空间很大。可以用到的合并方式很多,比如:

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

但是,这里说的mapping小技巧,是自己发现的,有不对的地方,欢迎大神指正。

>>>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
之后我们在引用的时候可以直接抓取需要的年龄对应的那个平均数.
>此处的df_map[str(x['Age'])]便是我们要抓取的用于计算的平均数。
>>>df_1['calulation']=df_1.apply(lambda x: x['BB']/df_map[str(x['Age'])],axis=1)

7. numpy.vectorize()

这个矢量化也很好用,专门处理数组运算,有点类似于pandas里的apply lambda。

详细解释:numpy.vectorize()

简单的用法就是把编辑好的function 放在里面,再去运行。很多时候一个dataframe里运算负荷太大,如果只抽取某一数组更为方便。如[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初级题中经常出现。

未完待续......如有错误,欢迎大神指正,一起进步学习~~~

你可能感兴趣的:(pandas/numpy数据结构算法(之行列变换)(二) (tag:行列转换,迪卡尔积,内置函数,数据结构))