美文网首页
第二章 Pandsa基础

第二章 Pandsa基础

作者: 陈易男 | 来源:发表于2020-12-19 15:09 被阅读0次

    本章主要学习的pandas的相关基础,如下图

    整章知识架构

    文件读写

    文件读写

    读取文件

    使用一段代码来进行演示

    # 读取csv文件
    df_csv = pd.read_csv('../data/my_csv.csv')
    # 读取txt文件
    df_txt = pd.read_table('../data/my_table.txt')
    # 读取excel文件
    df_excel = pd.read_excel('../data/my_excel.xlsx')
    # 如果不想将第一行当作列名,可以使用header=None
    df_excel = pd.read_excel('../data/my_excel.xlsx', header=None)
    # index_col表示把某一列或几列作为索引
    pd.read_csv('../data/my_csv.csv', index_col=['col1', 'col2'])
    

    文件写入

    一般在数据写入中,最常用的操作是把index设置为False,特别当索引没有特殊意义的时候,这样的行为能把索引在保存的时候去除。

    df_csv.to_csv('../data/my_csv_saved.csv', index=False)
    df_excel.to_excel('../data/my_excel_saved.xlsx', index=False)
    

    pandas中没有定义to_table函数,但是to_csv可以保存为txt文件,并且允许自定义分隔符,常用制表符\t分割:

    df_txt.to_csv('../data/my_txt_saved.txt', sep='\t', index=False)
    

    如果想要把表格快速转换为markdown和latex语言,可以使用to_markdown和to_latex函数,此处需要安装tabulate包。

    df_csv.to_markdown()
    df_csv.to_latex()
    

    基本数据结构

    基本数据结构

    Pandas中定义了两种基本的数据结构,Series和DataFrame

    • Series
      Series一般由四个部分组成,分别是序列的值data、索引index、存储类型dtype、序列的名字name。其中,索引也可以指定它的名字,默认为空。这四个属性可以通过以下的方式获取
    s = pd.Series(data = [100, 'a', {'dic1':5}],
                  index = pd.Index(['id1', 20, 'third'], name='my_idx'),
                  dtype = 'object',
                  name = 'my_name')
    # data
    s.value
    # 获取index
    s.index
    # 获取dtype
    s.dtype
    # 获取name
    s.name
    # 通过.shape可以获取序列的长度
    s.shape
    

    其中object代表一种混合数据类型

    • DataFrame

    DataFrame在Series的基础上增加了列索引。可以通过[index_item]获取对应的列的值

    data = [[1, 'a', 1.2], [2, 'b', 2.2], [3, 'c', 3.2]]
    df = pd.DataFrame(data = data,
                      index = ['row_%d'%i for i in range(3)],
                      columns=['col_0', 'col_1', 'col_2'])
    # 获取dataframe的值
    df.value
    # 获取dataframe的index
    df.index
    # 获取dataframe的column
    df.columns
    # 获取dataframe的shape
    df.shape
    # 获取指定的单列或多列
    df['col_1'] #单列
    df[['col_1', 'col_2']] #多列
    # 对dataframe进行转置
    df.T
    

    常用基本函数

    常用基本函数

    汇总函数
    headtail用于获取表或者序列的前n行和后n行,n的默认值是5
    infodescribe返回表的信息概况和表中数值列对应的主要统计量
    在后面的章节中将会提高具有更高信息概括功能的pandas-profiling

    df.head(5)
    df.tail(5)
    df.info()
    df.describe()
    

    特征统计函数
    最常见的是sum, mean, median, var, std, max, min

    """以下函数均可通过axis参数指定操作的方向,默认是axis=0"""
    df.sum()
    df.mean()
    df.median()
    df.var()
    df.std()
    df.max()
    df.min()
    

    quantile, cout, idxmax, idxmin返回的是分位数, 非缺失值个数, 最大值对应的索引及最小值对应的索引

    唯一值函数

    1. 对序列使用uniquenunique可以分别得到其唯一值组成的列表和唯一值的个数
    2. value_counts可以得到唯一值和其对应出现的频数:
    3. 如果想要观察多个列组合的唯一值,可以使用drop_duplicates。其中的关键参数是keep,默认值first表示每个组合保留第一次出现的所在行,last表示保留最后一次出现的所在行,False表示把所有重复组合所在的行剔除
    4. duplicateddrop_duplicates的功能类似,但前者返回了是否为唯一值的布尔列表,其keep参数与后者一致。其返回的序列,把重复元素设为True,否则为Falsedrop_duplicates等价于把duplicatedTrue的对应行剔除。

    替换函数(映射替换)

    1. replace中,可以通过字典构造,或者传入两个列表来进行替换
    2. replace还有一种特殊的方向替换,指定method参数为ffill则为用前面一个最近的未被替换的值进行替换,bfill则使用后面最近的未被替换的值进行替换
    df['Gender'].replace({'Female':0, 'Male':1}).head()
    s = pd.Series(['a', 1, 'b', 2, 1, 1, 'a'])
    s.replace([1, 2], method='ffill')
    s.replace([1, 2], method='bfill')
    

    替换函数(逻辑替换)
    逻辑替换包括了wheremask,这两个函数是完全对称的:where函数在传入条件为False的对应行进行替换,而mask在传入条件为True的对应行进行替换,当不指定替换值时,替换为缺失值。clip函数可以对超出范围的值进行截断

    s = pd.Series([-1, 1.2345, 100, -50])
    s.where(s<0)
    s.where(s<0, 100)
    s.mask(s<0)
    s.mask(s<0, -50)
    

    \color{red}{练一练}
    在 clip 中,超过边界的只能截断为边界值,如果要把超出边界的替换为自定义的值,应当如何做?

    def my_clip(s:pd.Series, min_bound, max_bound, left_value, right_value)->pd.Series:
        ret = s.clip(min_bound, max_bound)
        ret = ret.mask(ret==min_bound, left_value)
        ret = ret.mask(ret==max_bound, right_value)
        return ret
    

    排序函数
    排序共有两种方式,其一为值排序,其二为索引排序,对应的函数是sort_values和sort_index

    df_demo.sort_values(['Weight','Height'],ascending=[True,False]).head()
    df_demo.sort_index(level=['Grade','Name'],ascending=[True,False]).head()
    

    apply函数

    6. apply方法

    df_demo = df[['Height', 'Weight']]
    def my_mean(x):
         res = x.mean()
         return res
    df_demo.apply(my_mean)
    

    apply方法常用于DataFrame的行迭代或者列迭代,它的axis含义与第2小节中的统计聚合函数一致,apply的参数往往是一个以序列为输入的函数

    窗口对象

    窗口对象

    pandas中有3类窗口,分别是滑动窗口rolling、扩张窗口expanding以及指数加权窗口ewm

    s = pd.Series([1,2,3,4,5])
    roller = s.rolling(window = 3)
    roller
    

    \color{red}{练一练}
    rolling对象的默认窗口方向都是向前的,某些情况下用户需要向后的窗口,例如对1,2,3设定向后窗口为2的sum操作,结果为3,5,NaN,此时应该如何实现向后的滑窗操作?(提示:使用shift)

    s = pd.Series([1,2,3])
    s = s.rolling(2)
    s.sum().shift(-1)
    

    cummax, cumsum, cumprod函数是典型的类扩张窗口函数,请使用expanding对象依次实现它们。

    import numpy as np
    # cummax
    s.expanding().max()
    # cumsum
    s.expanding().sum()
    # cumprod
    s.expanding().apply(lambda x:np.prod(x))
    

    \color{red}{练习}

    Ex1:口袋妖怪数据集

    现有一份口袋妖怪的数据集,下面进行一些背景说明:

    • #代表全国图鉴编号,不同行存在相同数字则表示为该妖怪的不同状态

    • 妖怪具有单属性和双属性两种,对于单属性的妖怪,Type 2为缺失值

    • Total, HP, Attack, Defense, Sp. Atk, Sp. Def, Speed分别代表种族值、体力、物攻、防御、特攻、特防、速度,其中种族值为后6项之和

    1. HP, Attack, Defense, Sp. Atk, Sp. Def, Speed进行加总,验证是否为Total值。

    2. 对于#重复的妖怪只保留第一条记录,解决以下问题:

    • 求第一属性的种类数量和前三多数量对应的种类
    • 求第一属性和第二属性的组合种类
    • 求尚未出现过的属性组合
    1. 按照下述要求,构造Series
    • 取出物攻,超过120的替换为high,不足50的替换为low,否则设为mid
    • 取出第一属性,分别用replaceapply替换所有字母为大写
    • 求每个妖怪六项能力的离差,即所有能力中偏离中位数最大的值,添加到df并从大到小排序
    # 验证值之和是否与Total相等
    df['add'] = df[['HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed']].sum(axis=1)
    (df['add'] == df['Total']).all()
    
    # 去除多余的记录且只保留第一条记录
    unique_df = df.drop_duplicates(['#'], keep='first')
    # Type 1的种类数量
    print(unique_df['Type 1'].nunique())
    # Type 1前三多数量对应的种类
    print(unique_df['Type 1'].value_counts().index[:3])
    # 计算现在所有组合种类的数量
    combine = unique_df.drop_duplicates(['Type 1', 'Type 2'])
    num_unqiue_combine = combine.shape[0]
    # 生成所有的组合
    
    # 攻击值替换
    df['Attack'].mask(df['Attack']>120, 'high').mask(df['Attack']<50, 'low').mask((50<=df['Attack'])&(df['Attack']<=120), 'mid').head()
    # 取出第一属性
    Type1 = df['Type 1']
    # 使用replace进行大写替换
    # 构造映射字典
    map_dict = {}
    for index in unique_df['Type 1'].value_counts().index:
        map_dict[index] = index.upper()
    Type1.replace(map_dict)
    # 使用apply进行大写替换
    Type1.apply(lambda s: s.upper())
    
    # 计算六项能力的离差
    df['Deviation'] = df[['HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed']].apply(lambda x:np.max((x-x.median()).abs()), 1)
    df.sort_values('Deviation', ascending=False).head()
    
    # 源自答案
    L_full = [' '.join([i, j]) if i!=j else i for j in dp_dup['Type 1'].unique() for i in dp_dup['Type 1'].unique()]
    L_part = [' '.join([i, j]) if type(j)!=float else i for i, j in zip(attr_dup['Type 1'], attr_dup['Type 2'])]
    res = set(L_full).difference(set(L_part))
    len(res) # 
    

    Ex2:指数加权窗口

    1. 作为扩张窗口的ewm窗口

    在扩张窗口中,用户可以使用各类函数进行历史的累计指标统计,但这些内置的统计函数往往把窗口中的所有元素赋予了同样的权重。事实上,可以给出不同的权重来赋给窗口中的元素,指数加权窗口就是这样一种特殊的扩张窗口。

    其中,最重要的参数是alpha,它决定了默认情况下的窗口权重为w_i=(1−\alpha)^i,i\in\{0,1,...,t\},其中i=t表示当前元素,i=0表示序列的第一个元素。

    从权重公式可以看出,离开当前值越远则权重越小,若记原序列为x,更新后的当前元素为y_t,此时通过加权公式归一化后可知:

    \begin{split}y_t &=\frac{\sum_{i=0}^{t} w_i x_{t-i}}{\sum_{i=0}^{t} w_i} \\ &=\frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + (1 - \alpha)^{t} x_{0}}{1 + (1 - \alpha) + (1 - \alpha)^2 + (1 - \alpha)^{t-1}}\\\end{split}

    对于Series而言,可以用ewm对象如下计算指数平滑后的序列:

    np.random.seed(0)
    s = pd.Series(np.random.randint(-1,2,30).cumsum())
    s.head()
    

    使用expanding窗口实现

    def ewm_func(x, alpha=0.2):
        weight = np.array([(1 - alpha)**i for i in range(x.shape[0]-1, -1, -1)])
        return (x * weight).sum() / weight.sum()
    s.expanding().apply(ewm_func).head()
    
    1. 作为滑动窗口的ewm窗口(参考了答案)

    从第1问中可以看到,ewm作为一种扩张窗口的特例,只能从序列的第一个元素开始加权。现在希望给定一个限制窗口n,只对包含自身最近的n个窗口进行滑动加权平滑。请根据滑窗函数,给出新的wiyt的更新公式,并通过rolling窗口实现这一功能。

    新的权重为w_i = (1 - \alpha)^i, i\in \{0,1,...,n-1\}y_t更新如下:
    \begin{split}y_t &=\frac{\sum_{i=0}^{n-1} w_i x_{t-i}}{\sum_{i=0}^{n-1} w_i} \\ &=\frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + (1 - \alpha)^{n-1} x_{t-(n-1)}}{1 + (1 - \alpha) + (1 - \alpha)^2 + (1 - \alpha)^{n-1}}\\\end{split}

    s.rolling(window=4).apply(ewm_func).head() # 无需对原函数改动
    

    相关文章

      网友评论

          本文标题:第二章 Pandsa基础

          本文链接:https://www.haomeiwen.com/subject/wshcnktx.html