美文网首页
数据分析-万字入门(Pandas)

数据分析-万字入门(Pandas)

作者: CoderInsight | 来源:发表于2023-02-10 11:39 被阅读0次

    一、文件的读写

    1.csv文件

    demo.csv文件内容:

    id,name
    1,word
    2,word
    3,word
    4,word
    5,null
    6,NaN
    7,word
    8,word
    

    (1).读取.csv文件

    df = pd.read_csv(path, index_col= 0, header= 0)

    header = 0  # 源文件第一行作为读取后的列索引
    header = None # 源文件没有列索引,自动为其添加
    index_col = None # 源文件没有行索引,自动为其添加,把原来其自己的行索引视为新的一列
    index_col = 0  # 指定源文件第一列作为行索引
    sep=',' # 指定读取文件的分割方式为","(默认的也是, 逗号)
    
    1).代码示例
    import pandas as pd
    # 读取指定路径下的文件,并设置以第一行作为列索引
    df = pd.read_csv(r'D:\Project\Python\jyputerTest\demo.csv',index_col=0)
    print(df)
    # 检测数据类型是否是DataFrame
    print(type(df))
    # 输出:<class 'pandas.core.frame.DataFrame'>
    

    (2).输出.csv文件

    df.to_csv('grades.csv',columns=['客观分','主观分'],index=True,header=True)

    columns # 参数选择列,这里选择‘客官分’‘主观分’两个列,忽略了索引列(0,1,2,3,4,5,6.......)
    index=False # 在保存文件时,会忽略索引列,并且把原来的表覆盖掉,保存所选取的列
    index=True # 在保村文件时,会保留索引列(0,1,2,3,4,5.......)
    header=True # 在保存文件时,保留第一行的头信息
    header=False # 在保留文件时,不保留第一行的头信息
    
    1).代码示例
    # 将df中的指定列的数据写到csv文件中,并设置输出列索引(因为此时的列索引是我们的第一列数据),设置输出头部
    df.to_csv('grades.csv',columns=['name'],index=True,header=True)
    

    2.Excel文件

    对Excel文件进行操作需要 xlrd 、 openpyxl 库的依赖

    demo.xlsx文件内容

    id  name
    1   zhangsanfeng
    2   zhangsanfeng1
    3   zhangsanfeng2
    4   zhangsanfeng3
    5   zhangsanfeng4
    6   zhangsanfeng5
    7   zhangsanfeng6
    8   zhangsanfeng7
    

    (1).读取Excel文件

    1).代码示例
    # 读取指定路径下的Excel文件,并把第一列值作为索引读入
    df_xlsx = pd.read_excel(r'D:\Project\Python\jyputerTest\demo.xlsx',index_col=0)
    

    (2).将DataFrame写入Excel

    1).代码示例
    # 创建一个Excel写对象,并指定文件名
    lxml
    
    writer = pd.ExcelWriter('demoOut.xlsx')
    # 将df中的内容写入到指定的Excel文件中,然后设置表格的sheet_name,在之后可以使用另一种方式去读取
    df.to_excel(writer, sheet_name='sheet1',index=True)
    # 最后要加上保存操作,否则会报错
    writer.save()
    
    2).此时生成的文件的第二种读取方式
    # 加载一个Excel文件对象
    df_xlsx3 = pd.ExcelFile(r'D:\Project\Python\jyputerTest\demoOut.xlsx')
    # 再讲Excel文件对象转成df对象,同时指定索引列为标的第一列数据
    newDf = df_xlsx3.parse('sheet1',index_col=0)
    # 展示数据
    newDf
    

    3.MySQL数据库

    我们需要使用这三个库来实现MySQL的读写

    • pandas
    • sqlalchemy
    • pymysql

    其中,pandas模块提供了read_sql_query()函数实现了对数据库的查询,to_sql()函数实现了对数据库的写入,并不需要实现新建MySQL数据表。sqlalchemy模块实现了与不同数据库的连接,而pymysql模块则使得Python能够操作MySQL数据库。

    create database test;
    use test;
    create table demo(id int, name varchar(20));
    insert into demo values(1,'张三丰');
    insert into demo values(1,'张三丰');
    insert into demo values(1,'张三丰');
    insert into demo values(2,'张三丰2');
    insert into demo values(2,'张三丰2');
    insert into demo values(2,'张三丰2');
    

    (1).读写MySQL

    1).代码示例
    # 导入必要模块
    import pandas as pd
    from sqlalchemy import create_engine
    
    # 初始化数据库连接,使用pymysql模块
    # MySQL的用户:root, 密码:111111, 端口:3306,数据库:test,指定编码为utf-8(防止出现乱码)
    engine = create_engine('mysql+pymysql://root:111111@localhost:3306/test?charset=utf8')
    # 查询语句,选出demo表中的所有数据(主义)
    sql = "select * from demo;"
    # read_sql_query的两个参数: sql语句, 数据库连接
    df = pd.read_sql_query(sql, engine)
    # 输出employee表的查询结果
    print(df)
    
    # 新建pandas中的DataFrame, 只有id,num两列
    df2 = pd.DataFrame({'id':[1,2,3,4],'num':[12,34,56,89]})
    # 将新建的DataFrame储存为MySQL中的数据表,不储存index列
    df2.to_sql('mydf', engine, index= False)
    # 将从mysql中读取的数据再写到mysql数据库中
    df.to_sql('demo2', engine, index=False)
    
    # 打印执行成功
    print('Read from and write to Mysql table successfully!')
    
    # 在msyql的客户端可以查询相应的表以及表中的内容
    

    二、DataFrame对象基本操作

    1.数据提取与其他操作

    (0).DataFrame的添加删除操作

    import pandas as pd
    import numpy as np
    from pandas import DataFrame
    # 创建一个df对象(或者是采用读取到的csv文件直接赋值给一个df对象)
    df=pd.DataFrame([[2,4,1,5],[3,1,4,5],[5,1,4,2]],columns=['b','a','d','c'],index=['one','two','three'])
    df2=DataFrame(np.arange(16).reshape((4,4)),index=['a','b','c','d'],columns=['one','two','three','four']) 
    # 新插入的行一定要加 index,不然会报错
    df2.loc['new'] = ['a','a','a','a']
    # 运行结果为
        one two three four
    a   0    1   2     3
    b   4    5   6     7
    c   8    9   10    11
    d   12   13  14    15
    new a    a   a     a
    # 在指定的位置新插入一列(而且必须指定插入位置)
    df.insert(0,'E',[11,12,13,14,15]) # 表示在第0列的位置插入一列名为'E'的一列数据,值为'11,12,13,14,15'
    # 或者是直接指定一个新的列名,然后df会默认在最后一列插入你指定的数据,所指定的数据行数必须同原始数据中的行数相同
    df['F'] = [1,2,3,4]
    # 删除指定列
    df.drop(columns=['B', 'C'])
    # 删除指定行
    df.drop(index=[0, 1])
    

    (1).根据索引或者列名取值

    # 1.整数作为索引:df.iloc[n],默认查找第n行
    df.iloc[0]
    # 2.按索引提取区域行数值(区间的值是左闭右开的)
    df.iloc[0:5]
    # 3.列表作为索引:查找列表中数字对应行号的数据,如,当输入[0,2]时,对应查找行号为0和2的数据,而不是0-2行
    df.iloc[[0,2]]
    # 4.按索引提取单行的数值
    df.loc[3]
    # 5.提取4日之前的所有数据
    df[:'2013-01-04']
    # 6.设置日期为索引(date这一列的数据都是日期格式的)
    df=df.set_index('date')
    # 7.重设索引
    '''
    将排序后的索引重新排序
    df.reset_index(drop)
    其中drop为布尔型值:
        True表示修改原始数据的索引。
        False保留原始数据索引序列。
    '''
    df.reset_index()
    # 8.如取Name列数据的前5行
    df['Name'][:5]
    print(df['Name'][:5])
    # 9.获取多列数据时需要传入一个列表对象
    cols = ['name', 'province_name', 'city_name', 'city_code', 'area', 'addr']
    # 10.获取多列数据的前2行数据
    df[cols][:2]
    print(df[cols])
    # 11.判断city列中的所有值是否有值为'beijing'
    df['city'].isin(['beijing'])
    # 12.判断city列里是否包含beijing和shanghai,然后将符合条件的数据提取出来
    df.loc[df['city'].isin(['beijing','shanghai'])]
    

    (2).查看DataFrame的前后几行

    # 1.展示前两条记录(根据需要显示条数)
    df.head(2)
    print(df.head(2))
    
    # 2.展示后三条记录
    df.tail(3)
    print(df.tail(3))
    

    (3).展示DataFrame列名

    # 1.展示列名
    col_names = df.columns
    print(col_names)
    
    # 2查看下col_names格式
    type(col_names)
    
    # 3.将col_names转化为list
    col_list = col_names.tolist()
    print(col_list)
    

    (4).矢量化操作(批量操作)

    # 对Age列批量加10,然后
    (df['Age']+10).head
    
    # 对Age列批量减20
    df['Age']-10
    

    2.常见操作示例小结

    (0).字符串(str)的处理

    print(df['股票代码']) # 取当前列的所有值
    print('sz000002'[:2]) # 根据索引取当前指定列的值
    # 加上str之后可以使用常见的字符串函数对整列进行操作;
    print(df['股票代码'].str[:2]) # 通过索引获取字符串中字符(不写默认是从0开始)
    print(df['股票代码'].str.upper())  # 将字符串中的小写字母转为大写字母。
    print(df['股票代码'].str.lower()) # 转换字符串中所有大写字符为小写。
    print(df['股票代码'].str.len())  # 计算字符串的长度,length
    df['股票代码'].str.strip()  # strip操作,把字符串两边的空格去掉
    print(df['股票代码'].str.contains('sh'))  # 判断字符串中是否包含某些特定字符
    print(df['股票代码'].str.replace('szzzz', 'sz'))  # 进行替换,将szzzz替换成sz;格式:str.replace(old, new[, max]),其中max是指定替换不超过max次
    print(df['新浪概念'].str.split(';'))  # 对字符串进行分割
    print(df['新浪概念'].str.split(';').str[:2])  # 分割后取第一个位置
    print(df['新浪概念'].str.split(';', expand=True))  # 分割后并且将数据分列
    
    # 8.字符串.str的常见操作    
    # 定义变量: name = 'abcdaaaefg'
        (1). strip() 去除前后端的空格
        (2).判断变量name是否以al开头,并输出  开头查找 startswith()
            print(name.startswith('ab'))
        (3).判断变量name是否以Nb结尾,并输出  结尾查找 endswith()
            print(name.endswith('Nb'))
        (4).将name变量中所有的a替换为 p  替换方法 replace(old,new)
            print(name.replace('a','p'))
        (5).判断name变量中对应的值'a'出现几次  查找元素出现的次数方法 count()
            print(name.count('a'))
        (6).判断name变量中前四位中'a'出现几次. 解释:先把name的前四位找出来,然后用count计算l的出现次数
            a = name[0:5]
            print(a.count('a'))
        (7).找到name变量中'f'的索引; index方法:找不到会报错;find方法:找不到返回-1
            print(name.index('f'))
            print(name.find('f'))
        (8).for循环遍历集合
            s='fsfaf'
            for i in s:
                print(i)
    
    # str()函数: 返回一个对象的string格式(也就是将对象转成字符串)
    str(object="", encoding='utf-8') # 可以通过object参数指定传入的对象
    str() # 也可以直接将对象传入
    

    (1).数据清洗

    1).去重清洗
    # (1),去重清洗:调用函数是先删除重复项,然后重新设置索引!
    # 从前往后查找和判断是否有重复值,返回值是布尔类型的
    df.duplicated() 
    # reset_index(drop=True):设置删除重复值之后重新设置索引,使索引顺序仍然是连续的
    df = df.drop_duplicates().reset_index(drop=True)
    
    # df3中有重复的行数,我们如何将重复的行数去除
    df3.drop_duplicates(
        subset=['收盘价', '交易日期'],  # subset参数用来单独指定根据哪类类数据来判断是否重复。若不指定,则用全部列的数据来判断是否重复
        # 在去除重复值的时候,我们是保留上面一行还是下面一行?first保留上面一行,last保留下面一行,False就是一行都不保留
        keep='first', 
        # inplace = True:不创建新的对象,直接对原始对象进行修改;
        # inplace = False:对数据进行修改,创建并返回新的对象承载其修改结果。
        inplace=True
    )
    print(df3)
    
    2).空值清洗
    ①.删除空值并重新设置索引
    # (1),删除空值(NaN):调用函数先删除空值,然后重新设置索引! ★★
    df = df.dropna().reset_index(drop=True)
    
    # 2.从mysql数据库中取值
    from sqlalchemy import create_engine
    import pymysql
    db = create_engine('mysql+pymysql://root:111111@localhost:3306/migrate')
    import pandas as pd
    
    df = pd.read_sql('testavg',db)
    # 直接调用df对象的dropna方法来对缺失值进行删除
    # dropna( axis = 0 /1 )参数axis表示轴选择,axis=0 代表行,axis=1 代表列。
    df.dropna()
    
    ②.使用默认值填充空值
    # 1.用默认值进行填充
    from sqlalchemy import create_engine
    import pymysql
    db = create_engine('mysql+pymysql://root:111111@localhost:3306/migrate')
    import pandas as pd
    # 数据集是从Mysql数据库中获取的
    df = pd.read_sql('testavg',db)
    # 直接调用df对象的fillna方法来对缺失值进行填充
    df.fillna(999999)
    
    # 2.特殊方法下的填充
    df = df.fillna('666')
    print(df.fillna(value='没有金叉死叉'))  # 直接将缺失值赋值为固定的值
    df['MACD_金叉死叉'].fillna(value=df['收盘价'], inplace=True)  # 直接将缺失值赋值其他列的数据
    print(df.fillna(method='ffill'))  # 向上寻找最近的一个非空值,以该值来填充缺失的位置,全称forward fill,非常有用
    print(df.fillna(method='bfill'))  # 向下寻找最近的一个非空值,以该值来填充确实的位置,全称backward fill
    
    ③.使用平均值填充空值
    # 3.用平均值进行填充
    from sqlalchemy import create_engine
    import pymysql
    db = create_engine('mysql+pymysql://root:111111@localhost:3306/migrate')
    import pandas as pd
    import numpy as np
    df = pd.read_sql('testavg',db)
    # df.isna()         判断出df对象中的空值(如果为空,则为true)
    # np.where(df.isna())       返回空值对应的行索引和列索引以series的方式封装在了元组中
    # np.where(df.isna())[0]        从元组中取出行索引(0代表行);
    #                               只写一个数字的时候是取一行,然后放在列表中就可以取多行,并且可以加逗号之后就取某一列。
    # df.iloc[np.where(df.isna())[0]]           根据索引将值取出来,返回的是一个新的df对象
    # for a in df.iloc[np.where(df.isna())[0]].itertuples(): 遍历df对象,返回的是元组,可以通过下标取出对应的值;并对其进行遍历(在遍历df对象的时候就可以采用这种方法)
    #
    # row[0]        得到每一个行索引(这是df对象的)
    # 下一行的3是指的第3列
    # df.iloc[row[0],3]     通过行索引和指定的列取的每个对应的df对象的值(也就是对应的每一个空值所在的位置)
    # df['jobname']         根据标签取值,取到当前标签下的所有值
    # df['jobname'].str.contains(row[3])        比较两个字符串是否向相等用contains方法,而且contains方法是在str下的;返回值是布尔类型的
    # df[df['jobname'].str.contains(row[3])]        取出当前比较的结果,得到的是布尔类型的值(此时就已经将row[3]中对应的每个属性的所有值进行分组了)
    # df[df['jobname'].str.contains(row[3])]        根据不同的布尔值,从而取出每组中的值
    # df[df['jobname'].str.contains(row[3])].mean()     对所有的数值型数据进行求平均值,
    # df[df['jobname'].str.contains(row[3])].mean()['salary']       只取出salary标签的平均值
    # int(df[df['jobname'].str.contains(row[3])].mean()['salary'])      强制转换成int类型的
    # df.iloc[row[0],3] = int(df[df['jobname'].str.contains(row[3])].mean()['salary'])      最后对空值赋值从而实现替换空值
    for row in df.iloc[np.where(df.isna())[0]].itertuples():
        df.iloc[row[0],3] = int(df[df['jobname'].str.contains(row[3])].mean()['salary'])
    print(df)
    
    ④.使用众数填充空值
    # 4.用众数进行填充
    from sqlalchemy import create_engine
    import pymysql
    db = create_engine('mysql+pymysql://root:111111@localhost:3306/migrate')
    import pandas as pd
    import numpy as np
    df = pd.read_sql('testavg',db)
    # df.isna()         判断出df对象中的空值(如果为空,则为true)
    # np.where(df.isna())       返回空值对应的行索引和列索引以series的方式封装在了元组中
    # np.where(df.isna())[0]        从元组中取出行索引(0代表行);
    #                               只写一个数字的时候是取一行,然后放在列表中就可以取多行,并且可以加逗号之后就取某一列。
    # df.iloc[np.where(df.isna())[0]]           根据索引将值取出来,这里是传入的列表,然后就可以取出多行来。
    # for a in df.iloc[np.where(df.isna())[0]].itertuples():        将DataFrame迭代为元祖;并对其进行遍历。
    #
    # row[0]        得到每一个行索引(这是df对象的)
    # df.iloc[row[0],3]     通过行索引和指定的列取的每个对应的df对象的值(也就是对应的每一个空值)
    # df['jobname']         根据标签取值,取到当前标签下的所有值
    # df['jobname'].str.contains(row[3])        比较两个字符串是否向相等用contains方法,而且contains方法是在str下的;返回值是布尔类型的
    # df[df['jobname'].str.contains(row[3])]        取出当前比较的结果,得到的是布尔类型的值(此时就已经将row[3]中对应的每个属性的所有值进行分组了)
    # df[df['jobname'].str.contains(row[3])]        根据不同的布尔值,从而取出每组中的值
    # df[df['jobname'].str.contains(row[3])]['salary']      取出salary标签的值
    # df[df['jobname'].str.contains(row[3])]['salary'].mode()       salary标签中出现最多的钱数,也就是众数
    # int(df[df['jobname'].str.contains(row[3])]['salary'].mode())      强制转换成int类型的
    # df.iloc[row[0],3] = int(df[df['jobname'].str.contains(row[3])]['salary'].mode())      最后对空值赋值从而实现替换空值
    for row in df.iloc[np.where(df.isna())[0]].itertuples():
        df.iloc[row[0],3] = int(df[df['jobname'].str.contains(row[3])]['salary'].mode())
    
    
    ⑤.使用中位数填充空值
    # 5.用中位数进行填充
    from sqlalchemy import create_engine
    import pymysql
    db = create_engine('mysql+pymysql://root:111111@localhost:3306/migrate')
    import pandas as pd
    import numpy as np
    df = pd.read_sql('testavg',db)
    # df.isna()         判断出df对象中的空值(如果为空,则为true)
    # np.where(df.isna())       返回空值对应的行索引和列索引以series的方式封装在了元组中
    # np.where(df.isna())[0]        从元组中取出行索引(0代表行);
    #                               只写一个数字的时候是取一行,然后放在列表中就可以取多行,并且可以加逗号之后就取某一列。
    # df.iloc[np.where(df.isna())[0]]           根据索引将值取出来,这里是传入的列表,然后就可以取出多行来。
    # for a in df.iloc[np.where(df.isna())[0]].itertuples():        将DataFrame迭代为元祖;并对其进行遍历。
    #
    # row[0]        得到每一个行索引(这是df对象的)
    # df.iloc[row[0],3]     通过行索引和指定的列取的每个对应的df对象的值(也就是对应的每一个空值)
    # df['jobname']         根据标签取值,取到当前标签下的所有值
    # df['jobname'].str.contains(row[3])        比较两个字符串是否向相等用contains方法,而且contains方法是在str下的;返回值是布尔类型的
    # df[df['jobname'].str.contains(row[3])]        取出当前比较的结果,得到的是布尔类型的值(此时就已经将row[3]中对应的每个属性的所有值进行分组了)
    # df[df['jobname'].str.contains(row[3])]        根据不同的布尔值,从而取出每组中的值
    # df[df['jobname'].str.contains(row[3])]['salary']      取出salary标签的值
    # df[df['jobname'].str.contains(row[3])]['salary'].median()     salary标签下的中位数
    # int(df[df['jobname'].str.contains(row[3])]['salary'].median())        强制转换成int类型的
    # df.iloc[row[0],3] = int(df[df['jobname'].str.contains(row[3])]['salary'].median())        最后对空值赋值从而实现替换空值
    # row       是代表每一行的数据
    for row in df.iloc[np.where(df.isna())[0]].itertuples():
        df.iloc[row[0],3] = int(df[df['jobname'].str.contains(row[3])]['salary'].median())
    
    
    ⑥.使用插值法填充空值
    from sqlalchemy import create_engine
    import pymysql
    db = create_engine('mysql+pymysql://root:111111@localhost:3306/migrate')
    import pandas as pd
    # 数据来源是MySQL
    df = pd.read_sql('testavg',db)
    # 调用interpolate()函数来实现插值法对数据进行填充
    df.salary = df.salary.interpolate()
    
    ⑦.将空值作为一个新的label处理
    from sqlalchemy import create_engine
    import pymysql
    db = create_engine('mysql+pymysql://root:111111@localhost:3306/migrate')
    import pandas as pd
    # 数据来源是MySQL
    df = pd.read_sql('testavg',db)
    # 设置一个新的lable存下空值
    df['空值'] = df.iloc[np.where(df.isna())[0]].salary
    
    
    ⑧.使用KNN填补缺失值
    from fancyimpute import KNN
    fill_knn = KNN(k=3).fit_transform(df)
    df2 = pd.DataFrame(fill_knn)
    print(df2.head())
    
    # out 
           0    1    2       3         4    5
    0  111.0  0.0  2.0   360.0  4.000000  1.0
    1  112.0  1.0  9.0  1080.0  3.000000  1.0
    2  113.0  1.0  9.0  1080.0  2.000000  1.0
    3  114.0  0.0  1.0   360.0 *3.862873 *1.0
    4  115.0  0.0  1.0   270.0  5.000000  1.0
    
    3).异常值处理
    ①判断是否有异常值
    import numpy as np
            # ser1表示传入DataFrame的某一列
            def three_sigma(ser):
                # 求的是某一列的平均值啊(也就u)
                mean_value = ser.mean()
                # 求标准差(也就是σ)
                std_value = ser1.std()
                # 位于(u-3σ,u+3σ)区间的数据是正常的,不在这个值的数据便是异常值,其中s值的是方差
                # 一旦发现异常值就标注为true,否则标注为false,所以这里的返回值是布尔值
                rule = (ser < mean_value-3*std_value)|(ser > ser.mean()+3*ser1.std())
                # 获取异常数据,因为当前列就是一个Series,可以直接用布尔值取数据
                outrange = ser[rule]
                # 将判断出来的异常值返回
                return outrange
    
    ②处理异常值(替换)
    # 这里的i是自动增长的,方括号中的内容是推导式,推导出data中所有的列名,得到的仍然是一个列表
    # 再利用for循环将值取出来,这样循环中i就是每一个列的列名,这样可以根据列名再取出对应的值
    for i in [x for x in df]:
        # print(three_sigma(df[i])) # 测试打印替换之后的值
        # 判断每一列中是否有异常值,并用一个变量保存
        # 这里遍历的是df的列名,然后循环的根据列名去取每一列的数据,然后传给异常值检测的函数
        res = three_sigma(df[i])
        # print(res.values) # 测试打印输出res的值
        # 检测出异常值之后,当前测试环境下是采用个的默认值进行填充
        df = df.replace(to_replace=res.values,value=33)
        ③处理异常值(删除)
        # 用法:
        DataFrame.drop(labels=None,axis=0, index=None, columns=None, inplace=False)
        '''
            参数说明:
                labels 就是要删除的行列的名字,也可以用列表给定
                axis 默认为0,指删除行,因此删除columns(列)时要指定axis=1;
                index 直接指定要删除的行
                columns 直接指定要删除的列
                inplace=False,默认该删除操作不改变原数据,而是返回一个执行删除操作后的新dataframe;
                inplace=True,则会直接在原数据上进行删除操作,删除后无法返回。
            '''
        # 例子:
        # 删除指定列
        df.drop(columns=['B', 'C'])
        # 删除指定行
        df.drop(index=[0, 1])
    
        # 先检测异常值,然后返回异常值的索引
        import numpy as np
        # ser1表示传入DataFrame的某一列
        def three_sigma(ser):
            # 求的是某一列的平均值啊(也就u)
            mean_value = ser.mean()
            # 求标准差(也就是σ)
            std_value = ser.std()
            # 位于(u-3σ,u+3σ)区间的数据是正常的,不在这个值的数据便是异常值,其中s值的是方差
            # 一旦发现异常值就标注为true,否则标注为false,所以这里的返回值是布尔值
            rule = (ser < mean_value-3*std_value)|(ser > ser.mean()+3*ser.std())
            # 获取异常数据,因为当前列就是一个Series,可以直接用布尔值取数据
            outrange = ser[rule]
            # 将检测出来的异常值的索引返回
            return outrange.index
        # 根据返回的索引值将含有异常值的数据行删除
        for i in [x for x in df]:
            res= three_sigma(df[i])
            df = df.drop(index=res.values)
    
    4).数据标准化

    是为了消除指标之间的量纲和取值范围差异的影响,需要进行标准化处理,按数据比例进行放缩,使之落入一个特定的区域,便于进行综合分析:

    原始数据格式:
    78 521 602 2863
    144 -600 -521 2245
    95 -457 468 -1283
    69 596 695 1054
    190 527 691 2051
    101 403 470 2487
    146 413 435 2571

    # 实现函数:
        def programmer_2():
            # 定义文件的路径
            datafile = path + '/data/normalization_data.xls'
            # 将利用Pandas将数据读取进来
            data = pd.read_excel(datafile, header=None)
            # 最小-最大规范化
            print((data - data.min()) / (data.max() - data.min()))
            # 零-均值规范会(也就是标准差标准化)
            print((data - data.mean()) / data.std())
            # 小数定标规范化
            print(data / 10**np.ceil(np.log10(data.abs().max())))
    
    5).连续属性的离散化

    连续属性的离散化就是在数据的取值范围内设定若干个离散的划分点,将取值范围划分为一些离散化的区间,最后用不同的符号或整数代表落在每个子区间中的数据值.所以,离散化涉及两个子任务:确定分类数、以及如何将连续属性值映射大朋这些分类值.

    # 聚类画图
        def programmer_3():
            datafile = path + '/data/discretization_data.xls'
            data = pd.read_excel(datafile)
            data = data[u'肝气郁结证型系数'].copy()
            k = 4
    
            # 方法一: 等宽离散化,直接对数组进行分类
            d1 = pd.cut(data, k, labels=range(k))
    
            # 方法二: 等频率离散化
            w = [1.0 * i / k for i in range(k + 1)]
            # percentiles表示特定百分位数,同四分位数
            w = data.describe(percentiles=w)[4:4 + k + 1]
            w[0] = w[0] * (1 - 1e-10)
            d2 = pd.cut(data, w, labels=range(k))
    
            # 方法三:使用Kmeans
            # 建立模型,指定程序执行的并发数是4
            kmodel = KMeans(n_clusters=k, n_jobs=4)
    
            kmodel.fit(data.values.reshape(len(data), 1))
            # 输出聚类中心,并且排序
            c = DataFrame(kmodel.cluster_centers_).sort_values(0)
    
            # 相邻两项求中点,作为边界点
            w = DataFrame.rolling(c, 2).mean().iloc[1:]
            # 加上首末边界点
            w = [0] + list(w[0]) + [data.max()]
            d3 = pd.cut(data, w, labels=range(k))
    
            def cluster_plot(d, k):
                plt.figure(figsize=(8, 3))
                for j in range(0, k):
                    plt.plot(data[d == j], [j for i in d[d == j]], 'o')
                plt.ylim(-0.5, k - 0.5)
                return plt
    
            cluster_plot(d1, k).show()
            cluster_plot(d2, k).show()
            cluster_plot(d3, k).show()
        
    
    6).对小数位数的精度处理
    (1)对于要求较小的精度
      将精度高的浮点数转换成精度低的浮点数。
      1.round()内置方法
    round()不是简单的四舍五入的处理方式。
      >>> round(2.5)
      2
      >>> round(1.5)
      2
      >>> round(2.675)
      3
      >>> round(2.675, 2)
      2.67
      round()如果只有一个数作为参数,不指定位数的时候,返回的是一个整数,而且是最靠近的整数(这点上类似四舍五入)。
    但是当出现.5的时候,两边的距离都一样,round()取靠近的偶数
    
    (2)如果需要精确小数位数到17位之后,参考以下博客:
    https://www.cnblogs.com/yfz1552800131/p/5363297.html    
    
    7).删除满足指定条件的元素
    df = df.drop(some labels)
    df = df.drop(df[<some boolean condition>].index)
    
    # 要删除列“score”<50的所有行:
    df = df.drop(df[df.score < 50].index)
    # 直接将原来的值进行替换
    df.drop(df[df.score < 50].index, inplace=True)
    # 多条件情况:
    # 可以使用操作符: | 只需其中一个成立, & 同时成立, ~ 表示取反,它们要用括号括起来。
    # 例如删除列“score<50 和>20的所有行
    df = df.drop(df[(df.score < 50) & (df.score > 20)].index)
    
    #  删除满足指定条件的元素
    # 1.设置条件
    con = test_result['killed_by'] == 'Down and Out'
    # 2.将条件添加到到其中
    test_result.drop(test_result[con].index)
    

    (2).筛选操作

    # 0.根据指定的条件,筛选出相关拿数据
    print(df['股票代码'] == 'sh000002') # 判断股票代码是否等于sz000002
    print(df[df['股票代码'] == 'sz000002'])  # 将判断为True的输出:选取股票代码等于sz000002的行
    print(df[df['股票代码'].isin(['sz000002', 'sz000003 ', 'sz000004'])])  # 选取股票代码等于sz000002,sz000003,sz000004的行
    print(df[df['收盘价'] >= 24.0])  # 选取收盘价大于24的行
    print(df[(df.index >= '03/12/2016') & (df.index <= '06/12/2016')])  # &:与操作(并且)
    print(df[(df.index >= '03/12/2016') | (df.index <= '06/12/2016')])  # |:或操作(或者)
    
    # 使用与,或,非三个条件配合大于,小于,等于对数据进行筛选,并进行计数和求和。
    # 1,使用"与"进行筛选
    df.loc[(df['age'] > 25) & (df['city'] == 'beijing'), ['id','city','age','category','gender']]
    # 2,使用"或"进行筛选
    df.loc[(df['age'] > 25) | (df['city'] == 'beijing'), ['id','city','age','category','gender']].sort(['age'])
    # 3,使用"非"条件进行筛选
    df.loc[(df['city'] != 'beijing'), ['id','city','age','category','gender']].sort(['id'])
    # 4,对筛选后的数据按city列进行计数
    df.loc[(df['city'] != 'beijing'), ['id','city','age','category','gender']].sort(['id']).city.count()
    # 5,使用query函数进行筛选
    df.query('city == ["beijing", "shanghai"]')
    # 6,对筛选后的结果按prince进行求和
    df.query('city == ["beijing", "shanghai"]').price.sum()
    

    (3).常见的数据统计与计算函数

    print(df['收盘价'].mean())  # 求单列的均值,返回一个数。会自动排除空值。
    print(df[['收盘价', '成交量']].mean())  # 求两列的均值,返回两个数,Series
    print(df[['收盘价', '成交量']])
    print(df[['收盘价', '成交量']].mean(axis=1))  # 求两列的均值,返回DataFrame。axis=0或者1要搞清楚。
    # axis=1,代表对整几列进行操作。axis=0(默认)代表对几行进行操作。实际中弄混很正常,到时候试一下就知道了。
    print(df['收盘价'].max())  # 最大值
    print(df['收盘价'].min())  # 最小值
    print(df['收盘价'].std())  # 标准差
    print(df['收盘价'].count())  # 非空的数据的数量
    print(df['收盘价'].median())  # 中位数
    print(df['收盘价'].quantile(0.25))  # 25%分位数
    print(df['收盘价'].cov())  # 协方差
    
    # 数据采样,计算标准差,协方差和相关系数
    # 简单的数据随机采样(也就是说从df中随机取出三组数)
    df.sample(n=3)
    # 手动设置采样权重
    weights = [0, 0, 0, 0, 0.5, 0.5] 
    df.sample(n=2, weights=weights)
    # 采样后不放回
    df.sample(n=6, replace=False)
    # 采样后放回
    df.sample(n=6, replace=True)
    #  数据表描述性统计
    df.describe().round(2).T # round函数设置显示小数位,T表示转置
    # 计算列的标准差
    df['price'].std()
    # 计算两个字段间的协方差
    df['price'].cov(df['m-point'])
    # 数据表中所有字段间的协方差
    df.cov()
    # 两个字段的相关性分析 ★★
    df['price'].corr(df['m-point']) #相关系数在-1到1之间,接近1为正相关,接近-1为负相关,0为不相关
    # 数据表的相关性分析
    df.corr()
    

    (4).排序操作

    df.sort_values(col,inplace,ascending)
        col          对col列进行排序
        inplace      布尔型值,是否原地操作。
                     True时,操作结果覆盖掉原数据,原数据被修改
                     False时,新建一个新数据,原数据未被修改
        ascending    布尔型值。升序降序。 False降序,True升序
    
    # coding=utf-8
    import pandas as pd
    import numpy as np
    # 以下实现排序功能。
    # 先创建一个Series对象和DataFrame对象
    s=pd.Series([3,4,1,6],index=['b','a','d','c'])
    df = pd.DataFrame([[2,4,1,5],[3,1,4,5],[5,1,4,2]],columns=['b','a','d','c'],index=['one','two','three'])
    df = pd.DataFrame([['2012-3-4','2013-2-4','2014-11-4','2016-2-24'],
                      ['2019-1-24','2018-12-4','2017-11-14','2017-2-24'],
                     ['2012-3-4','2013-2-4','2014-11-4','2016-2-24']],columns=['a','b','c','d'])
    print(df)
    print(s)
    # 降序排序,默认是升序排序★★
    list.sort(reverse=True)
    # 'series通过索引进行排序:'
    print(s.sort_index())
    # 'series通过值进行排序:'
    print(s.sort_values())
    # 'dataframe根据行索引进行降序排序(排序时默认升序,调节ascending参数):'
    print(df.sort_index(ascending=False))
    # 'dataframe根据列索引进行排序:'
    print(df.sort_index(axis=1))
    # 'dataframe根据指定的列的值进行排序:'
    print(df.sort_values(by='a'))
    # '通过多个列索引进行排序:'
    print(df.sort_values(by=['a','c']))
    # by参数指定按照那一列进行排序,acsending参数指定是升序还是降序,默认是升序即acsending=1,当acsending=0时降序
    print(df.sort_values(by=['交易日期'], ascending=1))  
    # 按照多列进行排序
    print(df.sort_values(by=['股票名称', '交易日期'], ascending=[1, 1]))  
    # 在指定排序字段的时候可以直接使用字段名进行排序,inplace参数表示在排序的之后修改原来df中的值,False则是只返回排序的结果
    df.sort_values("xxx",inplace=True)
    

    (5).数据汇总

    groupby 分组函数的使用

    # 测试1:
    # 主要是groupby函数是用来实现分组的
    # 分组之后的数据都是一个又一个元组:其中下标为0的位置是:分组后的小组名;
    # 下标为1的位置是对应当前小组下的所有的值(包括行和列),
    # 然后可以选出某一个列,那么这个列就是一个Series对象
    '''
    for x in df['2015':'2016'].groupby('Identity_categorie'):
        print(x[0]) # 元组的小组名
        print('=================================================1==============================================================')
        print(x[1].head()) # 元组小组名中的组员信息(包括行和列的所有值)
        print('=================================================2==============================================================')
        print(x[1].Money.head()) # 只取出某个小组中的Money列的前五行信息
    
    打印结果示例:
    其他人员
    =================================================1==============================================================
                   id Area_county Street Committee name Identity_categorie Physical_condition   Money
    Approval_time                                                                                    
    2016-05-01     14         铁西区   文汇街道      东营社区  周承荣               其他人员                 健全   560.0
    2015-10-01     16         铁西区   文汇街道      东营社区  王法梅               其他人员             四级智力残疾   940.0
    2016-08-01     25         铁西区   文汇街道      东营社区  王宜昌               其他人员             二级视力残疾  1120.0
    2016-08-01     33         铁西区   文汇街道      东营社区  宋嘉伟               其他人员                 其它   600.0
    2015-12-01     36         铁西区   文汇街道      东营社区  康乐修               其他人员                 其它   500.0
    =================================================2==============================================================
    Approval_time
    2016-05-01     560.0
    2015-10-01     940.0
    2016-08-01    1120.0
    2016-08-01     600.0
    2015-12-01     500.0
    Name: Money, dtype: float64
    
    
    # 测试2:
    # 创建一个测试对象
    df = pd.DataFrame({ 'A': ['a', 'b', 'a', 'c', 'a', 'c', 'b', 'c'], 
                        'B': [2, 8, 1, 4, 3, 2, 5, 9], 
                        'C': [102, 98, 107, 104, 115, 87, 92, 123]})
    Out[1]:
        A   B   C
    0   a   2   102
    1   b   8   98
    2   a   1   107
    3   c   4   104
    4   a   3   115
    5   c   2   87
    6   b   5   92
    7   c   9   123
    '''
    # 按A列分组(groupby),获取其他列的均值(mean)
    df.groupby('A').mean()
    '''
    Out[2]: 
         B           C
    A                 
    a  2.0  108.000000
    b  6.5   95.000000
    c  5.0  104.666667
    '''
    # 按先按A列再按B列进行分组,
    df.groupby(['A','B']).mean()
    '''
    Out[3]: 
           C
    A B     
    a 1  107
      2  102
      3  115
    b 5   92
      8   98
    c 2   87
      4  104
      9  123
    '''
    # 分组后,可以选取单列数据,或者多个列组成的列表(list)进行运算
    s = df.groupby('A')
    s['B'].mean() # 只选择B列
    s[['B','C']].mean() # 选择B列和C列,用一个列表封装起来
    '''
    Out[4]: 
    A
    a    2.0
    b    6.5
    c    5.0
    Out[5]: 
         B           C
    A                 
    a  2.0  108.000000
    b  6.5   95.000000
    c  5.0  104.666667
    '''
    # 可以针对不同的列选用不同的聚合方法
    s.agg({'B':'mean', 'C':'sum'})
    '''
    Out[6]: 
        B   C
    A       
    a   2.0 324
    b   6.5 190
    c   5.0 314
    '''
    

    (6).pandas中的"size,shape,len,count,value_counts"之间的不同使用

    # size
    size():计算数组和矩阵所有数据的个数 
    df = np.array([[1,2,3],[4,5,6]]) 
    np.size(df),返回值为 6 
    np.size(df,1),返回值为 3
    # shape
    shape():得到矩阵每维的大小 
    np.shape(df),返回值为 (2,3)
    # shape和size的另外用法
    shape和size既可以作为函数,也可以作为ndarray的属性 
    df.size,返回值为 6 
    df.shape,返回值为 (2,3)
    # len
    len():返回对象的长度,注意不是length()函数 
    len([1,2,3]),返回值为3 
    len([[1,2,3],[3,4,5]]),返回值为2
    # count
    count():计算包含对象个数(计算非空值的个数)
    list = [1,1,1,2]; list.count(1) 返回值为3 # 计算list中某元素出现的次数
    'asddf'.count('d'),返回值为2 # 计算字符串中某个字符出现的次数
    # value_counts
    pandas 的value_counts()函数可以对Series里面的每个值进行计数并且排序,其中空值是默认剔除掉的
    '''
    数据实例:
        区域   销售量
    0   四川  10
    1   重庆  20
    2   四川  30
    3   重庆  49
    4   云南  32
    5   重庆  43
    # 每个区域都被计数,并且默认从最高到最低做降序排列;
    (1)df['区域'].value_counts()
    out[1]
    重庆  3
    四川  2
    云南  1
    # 可以通过指定ascending=True:参数来实现升序排序;
    (2)df['区域'].value_counts(ascending=True)
    out[2]
    云南  1
    四川  2
    重庆  3
    # 如果想得出的计数占比,可以加参数normalize=True;
    (3)df['区域'].value_counts(normalize=True)
    out[3]
    重庆  0.500000
    四川  0.333333
    云南  0.166667
    # value_counts()返回的结果是一个Series数组,可以跟别的数组进行运算。
    # 另外每一个df对象拆分之后就是一个个的Series,然后可以进行一些其他的计算
    '''
    

    (7).pandas对日期的处理小结

    按日期筛选数据,按日期统计数据
    当然大前提是已经把数据读取进来了,存到了df对象中

    1).设置当前列为索引列
    # 当前df对象中含有一个date列,其中都是时间格式的数据
    # (1).先对数据进行类型转换,方便接下来的筛选数据和统计数据
    df['date'] = pd.to_datetime(df['date']) #将数据类型转换为日期类型
    df = df.set_index('date') # 将date设置为index
    
    2).按日期筛选数据
    ①.按年度获取数据
    # 获取指定年份的数据
    print(df['2013'].head(2)) # 获取2013年的前两行数据
    print(df['2013'].tail(2)) # 获取2013年的倒数两行数据
    print('---------获取2016至2017年的数据-----------')
    print(df['2016':'2017'].head(2))  #获取2016至2017年的数据
    print(df['2016':'2017'].tail(2))  #获取2016至2017年的数据
    # 获得指定年份段的数据
    print(df['2016':'2017'].head(2))  #获取2016至2017年的前两行数据
    print(df['2016':'2017'].tail(2))  #获取2016至2017年的倒数两行数据
    
    ②.获取某月的数据
    print(df['2013-11'])
    
    ③.获取具体某天的数据
    print('---------获取具体某天的数据-----------')
    # 获取具体某天的数据
    # Series对象可以直接获取
    print(s['2013-11-06'])
    # 获取具体某天的数据,用datafrme直接选取某天时会报错,而series的数据就没有问题
    # print(df['2013-11-06'])
    # 对于df对象,可以考虑用区间来获取某天的数据
    print(df['2013-11-06':'2013-11-06'])
    
    3).按日期统计数据
    ①.按年统计数据
    # 1.按年统计,但仍以完整的日期显示
    print(df.resample('AS').sum())
    # "AS"是每年第一天为开始日期, "A是每年最后一天
    '''
                number
    date              
    2013-01-01      51
    2014-01-01     453
    2015-01-01     743
    2016-01-01    1552
    2017-01-01      92
    '''
    
    # 2.按年统计数据并按年显示
    print(df.resample('AS').sum().to_period('A'))
    '''
          number
    date        
    2013      51
    2014     453
    2015     743
    2016    1552
    2017      92
    '''
    
    ②.按季度统计数据
    # 1.按季度统计数据,但仍以完整的日期显示
    # (head()在没有指定输出多少行,默认的是输出前5行)
    print(df.resample('Q').sum().head())
    # "QS"是每个季度第一天为开始日期, "Q"是每个季度最后一天
    '''
                number
    date              
    2013-12-31      51
    2014-03-31      73
    2014-06-30      96
    2014-09-30     136
    2014-12-31     148
    '''
    
    # 2.按季度统计数据并按季度显示
    print(df.resample('Q').sum().to_period('Q').head())
    '''
            number
    date          
    2013Q4      51
    2014Q1      73
    2014Q2      96
    2014Q3     136
    2014Q4     148
    '''
    
    ③.按月统计数据
    # 1.按月统计数据,但仍以完整的日期显示
    print(df.resample('M').sum().head())
    '''
                number
    date              
    2013-10-31      10
    2013-11-30      14
    2013-12-31      27
    2014-01-31      16
    2014-02-28       4
    '''
    # "MS"是每个月第一天为开始日期, "M"是每个月最后一天
    '''
                number
    date              
    2013-10-31      10
    2013-11-30      14
    2013-12-31      27
    2014-01-31      16
    2014-02-28       4
    '''
    
    # 2.按月统计并按月显示
    print(df.resample('M').sum().to_period('M').head())
    '''
             number
    date           
    2013-10      10
    2013-11      14
    2013-12      27
    2014-01      16
    2014-02       4
    '''
    
    ④.按周统计数据
    print(df.resample('w').sum().head())
    # "w"表示 week 即一周
    '''
                number
    date              
    2013-10-27     7.0
    2013-11-03     3.0
    2013-11-10     5.0
    2013-11-17     7.0
    2013-11-24     NaN
    '''
    
    4).时间差的数据转换
    # 这样计算时间差会出现单位:days
    res = pd.DataFrame(pd.to_datetime(data['LOAD_TIME']) - pd.to_datetime(data['FFP_DATE'])) # 234days
    # 只取时间,而不带days
    res_new = res.dt.days # 234
    # 这样可以将日期的数据转换成月份格式的数据()
    res.map(lambda x: x / np.timedelta64(30 * 24 * 60, 'm'))
    

    (8).Numpy的where的用法

    # np.where(condition, x, y)
    # 满足条件(condition),输出x,不满足输出y。
    # 如果prince列的值>3000,group列显示high,否则显示low;
    df['group'] = np.where(df['price'] > 3000,'high','low')
    # 只有条件 (condition),没有x和y,则输出满足条件 (即非0) 元素的坐标 (等价于numpy.nonzero)。
    a = np.array([2,4,6,8,10])
    np.where(a > 5)             # 返回索引
    '''(array([2, 3, 4], dtype=int64),)''' 
    a[np.where(a > 5)]              # 等价于 a[a>5]
    '''array([ 6,  8, 10])'''
    

    (9).数据表的合并操作

    # (1).merge
    '''
    多表之间的连接也是非常常见的数据库操作,连接分内连接和外连接,在数据库语言中通过join关键字实现.
    注意:默认情况下,merge函数实现的是两个表之间的内连接,即返回两张表中共同部分的数据。
    可以通过how参数设置连接的方式,left为左连接;right为右连接;outer为外连接。
    可以使用on关键字实现指定按某个列去合并!
    '''
    # 这里的df和df1是两个Dataframe对象
    df=pd.merge(df,df1,how='inner',on='name')  # 匹配合并:交集(这里两个df中都有name列)
    df_left=pd.merge(df,df1,how='left')        # 左连接(此时会保留左表(df)的数据)
    df_right=pd.merge(df,df1,how='right')     # 右连接(此时会保留右表(df1)的数据)
    df_outer=pd.merge(df,df1,how='outer')  # 并集(两张表取并集)
    # (2).append
    result = df1.append(df2)
    '''
    
    df1:
        A   B
    0   A0  B0
    1   A1  B1
    df2:
        A   B
    3   A3  B3
    4   A4  B4
    result:
        A   B
    0   A0  B0
    1   A1  B1
    3   A3  B3
    4   A4  B4
    '''
    # (3).join
    result = left.join(right, on='key') # 根据key列,将左表和右表联系在一起
    '''
    left:
        A   B
    0   A0  B0
    1   A1  B1
    right:
        C   D
    0   C3  D3
    1   C4  D4
    result:
        A   B   C   D
    0   A0  B0  C0  D0
    1   A1  B1  C1  D1
    '''
    # (4).concat
    frames = [df1, df2, df3] # 指定三个df对象
    result = pd.concat(frames)  # 将三个df对象的值合并到一个df对象中
    '''
    df1:
        A   B
    0   A0  B0
    1   A1  B1
    df2:
        A   B
    3   A3  B3
    4   A4  B4
    df3:
        A   B
    5   A5  B5
    6   A6  B6
    result:
        A   B
    0   A0  B0
    1   A1  B1
    3   A3  B3
    4   A4  B4
    5   A5  B5
    6   A6  B6
    '''
    

    相关文章

      网友评论

          本文标题:数据分析-万字入门(Pandas)

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