Pandas小记

pandas官方中文文档:https://www.pypandas.cn/

pandas官网:https://pandas.pydata.org/docs/

一、Series

类似于一维数组的对象,由一组索引和一组数据组成

创建、查询

import pandas as pd
import numpy as np

#使用列表创建
s1 = pd.Series([1,'a',2,5])
print(s1)
print(s1.index)
print(s1.values)

#指定索引方式创建
s2 = pd.Series([1,'a',2,5],index=['d','b','a','c'])
print(s2)

#字典的方式查询,根据索引
print(s2['a'])
#根据多个索引查询多个值,查询多个值返回的是另一个series
print(s2[['a','b']])

二、Dataframe

表格型数据结构,每列可以是不同的值类型;既有行索引index,还有列索引columns;可以看做Series组成的字典

dataframe读取一行或者一列返回series,查询多行多列还是会返回dataframe

df.info() 查看每一列的类型

查看多少行,多少列、类型等基本信息

object可能是列有多个类型值时,比如float的列中有部分空值

#   Column     Non-Null Count  Dtype 
---  ------     --------------  ----- 
 0   ymd        365 non-null    object
 1   bWendu     365 non-null    int32 
 2   yWendu     365 non-null    int32 
 3   tianqi     365 non-null    object
 4   fengxiang  365 non-null    object
 5   fengli     365 non-null    object
 6   aqi        365 non-null    int64 
 7   aqiInfo    365 non-null    object
 8   aqiLevel   365 non-null    int64 

2.1 创建

一般都是读取excel、csv文件、mysql数据库创建dataframe,也可以从多个字典序列创建

def dataframe_test():
    data = {
        "state":['1','2','3'],
        "year":[2020,2021,2022],
        "pop":[1.5,1.6,3.2]
    }
    df = pd.DataFrame(data)
    print(df)

结果:

  state  year  pop
0     1  2020  1.5
1     2  2021  1.6
2     3  2022  3.2

2.2 查询

df.columns返回一个index类型对象,df.index返回一个rangeIndex对象,都可以使用for来循环

colums = df.columns
for i in colums:
    print(i)

for i in df.index:
    print(i)
查询前几行和后几行

df.head()/df.head(2)

df.tail()/df.tail(2)

采样几行(随机)

df.sample(10)

设置index

df.set_index()

import pandas as pd

df = pd.read_csv('./beijing_tianqi_2018.csv')
print(df.index)
df.set_index('ymd',inplace=True)
print(df.head()

结果

           bWendu yWendu tianqi fengxiang fengli  aqi aqiInfo  aqiLevel
ymd                                                                    
2018-01-01     3℃    -6℃   晴~多云       东北风   1-2级   59       良         2
2018-01-02     2℃    -5℃   阴~多云       东北风   1-2级   49       优         1
2018-01-03     2℃    -5℃     多云        北风   1-2级   28       优         1
2018-01-04     0℃    -8℃      阴       东北风   1-2级   28       优         1
2018-01-05     3℃    -6℃   多云~晴       西北风   1-2级   50       优         1

查看每一列的值类型

print(df.dtypes)

查询列

df[[‘bWendu’,‘yWendu’]]可以查询出两列的dataFrame

查询行

df.loc[0]查询第一行数据

df.loc[1:2]查询第二三行数据

2.2.1 .loc函数

能查询也能覆盖写入

使用df.loc将bWendu(最高)、yWendu(最低)温度的摄氏度符号去掉
df.loc[:,'bWendu'] = df['bWendu'].str.replace('℃','').astype('int32')
df.loc[:,'yWendu'] = df['yWendu'].str.replace('℃','').astype('int32')
print(df.head())

结果:

            bWendu  yWendu tianqi fengxiang fengli  aqi aqiInfo  aqiLevel
ymd                                                                      
2018-01-01       3      -6   晴~多云       东北风   1-2级   59       良         2
2018-01-02       2      -5   阴~多云       东北风   1-2级   49       优         1
2018-01-03       2      -5     多云        北风   1-2级   28       优         1
2018-01-04       0      -8      阴       东北风   1-2级   28       优         1
2018-01-05       3      -6   多云~晴       西北风   1-2级   50       优         1

2.2.1.1 使用单个label值查询数据

loc第一个参数是index

print(df.loc['2018-01-03',['bWendu']])
print(b['bWendu'])

结果:

bWendu    2
Name: 2018-01-03, dtype: object
2
bWendu     2
yWendu    -5
Name: 2018-01-03, dtype: object
2.2.1.2 使用值列表批量查询数据
查询单个列
print(df.loc[['2018-01-03','2018-01-04','2018-01-05'],'bWendu'])

返回一个series

ymd
2018-01-03    2
2018-01-04    0
2018-01-05    3
Name: bWendu, dtype: int32
查询多个列
print(df.loc[['2018-01-03','2018-01-04','2018-01-05'],['bWendu','yWendu']])

返回个dataframe

            bWendu  yWendu
ymd                       
2018-01-03       2      -5
2018-01-04       0      -8
2018-01-05       3      -6
2.2.1.3 使用数值区间进行范围查询(既包含开始也包含结束闭区间)
行index按区间
print(df.loc['2018-01-03':'2018-01-05','bWendu'])

返回:

ymd
2018-01-03    2
2018-01-04    0
2018-01-05    3
Name: bWendu, dtype: int32
列index按区间
print(df.loc['2018-01-03','bWendu':'fengxiang'])

返回:

bWendu        2
yWendu       -5
tianqi       多云
fengxiang    北风
Name: 2018-01-03, dtype: object
2.2.1.4 传入条件表达式查询

loc第一个参数是bool列表,bool列表长度得等于行数或者列数

1.查询全年最低温度低于-10度的列表
print(df.loc[df['yWendu']<-10,:])

结果:

            bWendu  yWendu tianqi fengxiang fengli  aqi aqiInfo  aqiLevel
ymd                                                                      
2018-01-23      -4     -12      晴       西北风   3-4级   31       优         1
2018-01-24      -4     -11      晴       西南风   1-2级   34       优         1
2018-01-25      -3     -11     多云       东北风   1-2级   27       优         1
2018-12-26      -2     -11   晴~多云       东北风     2级   26       优         1
2018-12-27      -5     -12   多云~晴       西北风     3级   48       优         1
2018-12-28      -3     -11      晴       西北风     3级   40       优         1
2018-12-29      -3     -12      晴       西北风     2级   29       优         1
2018-12-30      -2     -11   晴~多云       东北风     1级   31       优         1

布尔表达式的数据样式:

print(df['yWendu']<-10)

结果是365长度的series,和数据行数一样。所以loc是会布尔列表返回True那些行的数据。

ymd
2018-01-01    False
2018-01-02    False
2018-01-03    False
2018-01-04    False
2018-01-05    False
              ...  
2018-12-27     True
2018-12-28     True
2018-12-29     True
2018-12-30     True
2018-12-31    False
Name: yWendu, Length: 365, dtype: bool

Process finished with exit code 0

2.查询完美天气

完美天气=高温小于30、低温大于15、晴天、天气质量优

print(df.loc[(df['bWendu'] < 30) & (df['yWendu']>15) & (df['tianqi'] == '晴') & (df['aqiLevel']==1),:])

结果:

            bWendu  yWendu tianqi fengxiang fengli  aqi aqiInfo  aqiLevel
ymd                                                                      
2018-09-07      27      16      晴       西北风   3-4级   22       优         1
2.2.1.5 调用函数查询
print(df.loc[lambda df:(df['bWendu'] <= 30) & (df['yWendu']>=15) & (df['tianqi'] == '晴') & (df['aqiLevel']==1),:])

结果:


            bWendu  yWendu tianqi fengxiang fengli  aqi aqiInfo  aqiLevel
ymd                                                                      
2018-08-24      30      20      晴        北风   1-2级   40       优         1
2018-09-07      27      16      晴       西北风   3-4级   22       优         1
或者自定义函数

函数编程的本质:函数自身可以像变量一样传递

def query_data(df):
    return df.index.str.startswith('2018-09') & df['aqiLevel']==1

print(df.loc[query_data,:])

返回:

            bWendu  yWendu tianqi fengxiang fengli  aqi aqiInfo  aqiLevel
ymd                                                                      
2018-09-01      27      19   阴~小雨        南风   1-2级   50       优         1
2018-09-04      31      18      晴       西南风   3-4级   24       优         1
2018-09-05      31      19   晴~多云       西南风   3-4级   34       优         1
2018-09-06      27      18   多云~晴       西北风   4-5级   37       优         1
2018-09-07      27      16      晴       西北风   3-4级   22       优         1
2018-09-08      27      15   多云~晴        北风   1-2级   28       优         1
2018-09-13      29      20   多云~阴        南风   1-2级  107    轻度污染         3
2018-09-14      28      19  小雨~多云        南风   1-2级  128    轻度污染         3
2018-09-15      26      15     多云        北风   3-4级   42       优         1
2018-09-16      25      14   多云~晴        北风   1-2级   29       优         1
2018-09-17      27      17   多云~阴        北风   1-2级   37       优         1
2018-09-18      25      17   阴~多云       西南风   1-2级   50       优         1
2018-09-21      25      14      晴       西北风   3-4级   50       优         1
2018-09-22      24      13      晴       西北风   3-4级   28       优         1
2018-09-23      23      12      晴       西北风   4-5级   28       优         1
2018-09-24      23      11      晴        北风   1-2级   28       优         1
2018-09-25      24      12   晴~多云        南风   1-2级   44       优         1
2018-09-29      22      11      晴        北风   3-4级   21       优         1
2018-09-30      19      13     多云       西北风   4-5级   22       优         1

Process finished with exit code 0

2.3 新增、修改

2.3.1 df.loc函数

修改两列的值

df.loc[:,'bWendu'] = df['bWendu'].str.replace('℃','').astype('int32')
df.loc[:,'yWendu'] = df['yWendu'].str.replace('℃','').astype('int32')

新增一列

df.loc[:,'wencha'] = df['bWendu'] - df['yWendu']
print(df.head())
2.3.2 df.apply函数

apply方法,第一个参数是函数,函数返回新列(行)中需要的值;第二个参数axis 0-行 1-列。apply方法返回一个series,可以赋值到行或者列

def get_wendu_type(x):
    if x['bWendu'] > 33:
        return '高温'
    if x['yWendu'] < -10:
        return '低温'
    return '常温'
df.loc[:,'wendu_type'] = df.apply(get_wendu_type,axis=1)
print(df.head())
#value_counts函数统计一列中每个值的数量
print(df['wendu_type'].value_counts())

结果:

            bWendu  yWendu tianqi  ... aqiLevel wencha  wendu_type
ymd                                ...                            
2018-01-01       3      -6   晴~多云  ...        2      9          常温
2018-01-02       2      -5   阴~多云  ...        1      7          常温
2018-01-03       2      -5     多云  ...        1      7          常温
2018-01-04       0      -8      阴  ...        1      8          常温
2018-01-05       3      -6   多云~晴  ...        1      9          常温

[5 rows x 10 columns]
常温    328
高温     29
低温      8
Name: wendu_type, dtype: int64

Process finished with exit code 0

2.3.2.1 df.apply给表格添加多列
def split_column(line):
    line["姓名"], line["性别"], line["年龄"], line["籍贯"]  = line["数据"].split(":")
    return line
df = df.apply(split_column, axis=1)


2.第二种

import pandas as pd

df = pd.read_excel("./c23_excel_vlookup/学生信息表.xlsx")
print(df.head())


def new_columns(row):
    newa, newb = row['年龄'] + row['年龄'], row['年龄'] * row['年龄']
    return newa, newb
# expand参数会将函数返回的元组拆成两列
df[['newa', 'newb']] = df.apply(new_columns, axis=1, result_type="expand")
print(df.head())
     学号  姓名 性别  年龄  籍贯
0  S001  怠涵  女  23  山东
1  S002  婉清  女  25  河南
2  S003  溪榕  女  23  湖北
3  S004  漠涓  女  19  陕西
4  S005  祈博  女  24  山东
     学号  姓名 性别  年龄  籍贯  newa  newb
0  S001  怠涵  女  23  山东    46   529
1  S002  婉清  女  25  河南    50   625
2  S003  溪榕  女  23  湖北    46   529
3  S004  漠涓  女  19  陕西    38   361
4  S005  祈博  女  24  山东    48   576

Process finished with exit code 0

2.3.3 df.assign方法

x是一个行series。assign方法只会新增列(yWendu_huahsi,bWendu_huashi),返回一个新的dataframe,不会修改原df的值

df1 = df.assign(
    #摄氏温度转华氏温度
    yWendu_huashi = lambda x:x['yWendu'] * 9/5 + 32,
    bWendu_huashi = lambda x:x['bWendu'] * 9/5 + 32
)

print(type(df1))
print(df1.head())

结果:

<class 'pandas.core.frame.DataFrame'>
            bWendu  yWendu tianqi  ... wendu_type yWendu_huashi  bWendu_huashi
ymd                                ...                                        
2018-01-01       3      -6   晴~多云  ...         常温          21.2           37.4
2018-01-02       2      -5   阴~多云  ...         常温          23.0           35.6
2018-01-03       2      -5     多云  ...         常温          23.0           35.6
2018-01-04       0      -8      阴  ...         常温          17.6           32.0
2018-01-05       3      -6   多云~晴  ...         常温          21.2           37.4
2.3.4 按条件选择分组分别赋值
df['wencha_type'] = ''
df.loc[df['bWendu']-df['yWendu'] > 10,'wencha_type'] = "温差大"
df.loc[df['bWendu']-df['yWendu'] <= 10,'wencha_type'] = "温差正常"
print(df['wencha_type'].value_counts())

2.4 Pandas数据统计函数

2.4.1 df.describe() 查看每列的平均值、最大最小值、中位数等统计信息

一下提取所有数字列统计结果

print(df.describe())

结果:

count-数量、mean-平均值、std-标准差、min-最小值、max-最大值

           bWendu      yWendu         aqi    aqiLevel      wencha
count  365.000000  365.000000  365.000000  365.000000  365.000000
mean    18.665753    8.358904   82.183562    2.090411   10.306849
std     11.858046   11.755053   51.936159    1.029798    2.781233
min     -5.000000  -12.000000   21.000000    1.000000    2.000000
25%      8.000000   -3.000000   46.000000    1.000000    8.000000
50%     21.000000    8.000000   69.000000    2.000000   10.000000
75%     29.000000   19.000000  104.000000    3.000000   12.000000
max     38.000000   27.000000  387.000000    6.000000   18.000000

取中位数

df['a'].median()
查看单个series的数据
print(df['bWendu'].min())
print(df['bWendu'].max())
print(df['bWendu'].mean())
-5
38
18.665753424657535
2.4.2 唯一去重和按值计数
2.4.2.1 唯一性去重

一般不用于数值列,而是枚举、分类列

print(df['fengxiang'].unique())
print(df['tianqi'].unique())
print(df['fengli'].unique())
['东北风' '北风' '西北风' '西南风' '南风' '东南风' '东风' '西风']
['晴~多云' '阴~多云' '多云' '阴' '多云~晴' '多云~阴' '晴' '阴~小雪' '小雪~多云' '小雨~阴' '小雨~雨夹雪'
 '多云~小雨' '小雨~多云' '大雨~小雨' '小雨' '阴~小雨' '多云~雷阵雨' '雷阵雨~多云' '阴~雷阵雨' '雷阵雨'
 '雷阵雨~大雨' '中雨~雷阵雨' '小雨~大雨' '暴雨~雷阵雨' '雷阵雨~中雨' '小雨~雷阵雨' '雷阵雨~阴' '中雨~小雨'
 '小雨~中雨' '雾~多云' '霾']
['1-2级' '4-5级' '3-4级' '2级' '1级' '3级']
2.4.2.2 按值计数
print(df['fengxiang'].value_counts())
南风     92
西南风    64
北风     54
西北风    51
东南风    46
东北风    38
东风     14
西风      6
Name: fengxiang, dtype: int64
2.4.2 相关系数和协方差

用途:

  1. 两只股票是不是同涨同跌?程度多大?正相关还是负相关?
  2. 产品销量的波动,跟那些因素正相关、负相关,程度多大?

对于X、Y两个变量

  1. 协方差:衡量同向反向程度,如果协方差为正,说明X、Y同向变化,协方差越大说明同向程度越高;如果协方差为负,说明X、Y反向程度越高。
  2. 相关系数:衡量相似程度,当他们的相关系数为1时,说明两个变量变化时的正向相似度最大,当相关系数为-1时,说明两个变量变化的反向相似度最大。
print(df.cov())
print(df.corr())
              bWendu      yWendu          aqi   aqiLevel     wencha
bWendu    140.613247  135.529633    47.462622   0.879204   5.083614
yWendu    135.529633  138.181274    16.186685   0.264165  -2.651641
aqi        47.462622   16.186685  2697.364564  50.749842  31.275937
aqiLevel    0.879204    0.264165    50.749842   1.060485   0.615038
wencha      5.083614   -2.651641    31.275937   0.615038   7.735255
            bWendu    yWendu       aqi  aqiLevel    wencha
bWendu    1.000000  0.972292  0.077067  0.071999  0.154142
yWendu    0.972292  1.000000  0.026513  0.021822 -0.081106
aqi       0.077067  0.026513  1.000000  0.948883  0.216523
aqiLevel  0.071999  0.021822  0.948883  1.000000  0.214740
wencha    0.154142 -0.081106  0.216523  0.214740  1.000000
查看单独的列的相关系数
print(df['aqi'].corr(df['bWendu']))
print(df['aqi'].corr(df['bWendu']-df['yWendu']))
0.07706705916811069
0.2165225757638205

2.5 Pandas对缺失值的处理

  1. isnull和notnull:检测是否是空值,可用于df和series
  2. dropna:丢弃、删除缺失值
    1. axis:删除行还是列,{0 or ‘index’,1 or ‘columns’},default 0
    2. how:如果等于any则任何值为空都删除,如果等于all则所有值都为空才删除
    3. inplace:如果为True则修改当前df,否则返回新的df
  3. fillna:填充空值
    1. value:用于填充的值,可以是单值,或者字典(key是列名,value是值)
    2. method:等于ffill使用前一个不为空的值填充forword fill;等于bfill使用后一个不为空的值填充backword fill
    3. axis:按行还是按列填充,{0 or ‘index’,1 or ‘columns’}
    4. inplace:如果为True则修改当前df,否则返回新的df
2.5.1 判断空值

read_excel方法中skiprows=2表示跳过前两个列(学生信息中前两个为空列)

import pandas as pd
#skiprows=2表示跳过前两个列(学生信息中前两个为空列)
df = pd.read_excel('./student_excel.xlsx',skiprows=2)
print(df)
print(df.isnull())
# 加和处理,False=0 True=1,展示每列有多少缺失值。0就是没有
print(df.isnull().sum())
    Unnamed: 0   姓名   科目    分数
0          NaN   小明   语文  85.0
1          NaN  NaN   数学  80.0
2          NaN  NaN   英语  90.0
3          NaN  NaN  NaN   NaN
4          NaN   小王   语文  85.0
5          NaN  NaN   数学   NaN
6          NaN  NaN   英语  90.0
7          NaN  NaN  NaN   NaN
8          NaN   小刚   语文  85.0
9          NaN  NaN   数学  80.0
10         NaN  NaN   英语  90.0
    Unnamed: 0     姓名     科目     分数
0         True  False  False  False
1         True   True  False  False
2         True   True  False  False
3         True   True   True   True
4         True  False  False  False
5         True   True  False   True
6         True   True  False  False
7         True   True   True   True
8         True  False  False  False
9         True   True  False  False
10        True   True  False  False

单个列上判断null

print(df['分数'].isnull())
print(df['分数'].notnull())
0     False
1     False
2     False
3      True
4     False
5      True
6     False
7      True
8     False
9     False
10    False
Name: 分数, dtype: bool

1      True
2      True
3     False
4      True
5     False
6      True
7     False
8      True
9      True
10     True
Name: 分数, dtype: bool

结合之前loc函数

print(df.loc[df['分数'].notnull(),:])

打印出了分数不为null的几行数据:

    Unnamed: 0   姓名  科目    分数
0          NaN   小明  语文  85.0
1          NaN  NaN  数学  80.0
2          NaN  NaN  英语  90.0
4          NaN   小王  语文  85.0
6          NaN  NaN  英语  90.0
8          NaN   小刚  语文  85.0
9          NaN  NaN  数学  80.0
10         NaN  NaN  英语  90.0
2.5.2 删除掉全是空值的列

丢弃掉了全为null的第一列

df.dropna(axis='columns',how='all',inplace=True)
print(df)
     姓名   科目    分数
0    小明   语文  85.0
1   NaN   数学  80.0
2   NaN   英语  90.0
3   NaN  NaN   NaN
4    小王   语文  85.0
5   NaN   数学   NaN
6   NaN   英语  90.0
7   NaN  NaN   NaN
8    小刚   语文  85.0
9   NaN   数学  80.0
10  NaN   英语  90.0

删除全为空值的行

df.dropna(axis='index',how='all',inplace=True)
print(df)
     姓名  科目    分数
0    小明  语文  85.0
1   NaN  数学  80.0
2   NaN  英语  90.0
4    小王  语文  85.0
5   NaN  数学   NaN
6   NaN  英语  90.0
8    小刚  语文  85.0
9   NaN  数学  80.0
10  NaN  英语  90.0
2.5.3 将分数列为空的填充为0
df = df.fillna({'分数':0})
#等同于 
df.loc[:,'分数'] = df['分数'].fillna(0)

print(df)

返回个新的dataframe

     姓名  科目    分数
0    小明  语文  85.0
1   NaN  数学  80.0
2   NaN  英语  90.0
4    小王  语文  85.0
5   NaN  数学   0.0
6   NaN  英语  90.0
8    小刚  语文  85.0
9   NaN  数学  80.0
10  NaN  英语  90.0
2.5.4 将姓名的缺失值填充

使用空值前面的值填充

df.loc[:,'姓名'] = df['姓名'].fillna(method='ffill')
print(df)
    姓名  科目    分数
0   小明  语文  85.0
1   小明  数学  80.0
2   小明  英语  90.0
4   小王  语文  85.0
5   小王  数学   0.0
6   小王  英语  90.0
8   小刚  语文  85.0
9   小刚  数学  80.0
10  小刚  英语  90.0
2.5.5 将清洗好的Excel保存

index=False表示不把自动生成的最左侧的数字列写入到Excel,执行完之后会在当前目录生成一个Excel文件,如果文件已存在会报错。

df.to_excel('./studen_excel_clean.xlsx',index=False)

2.6 Pandas的SettingWithCopyWarning怎么处理

原因:对dataframe先筛选再set,pandas不能判断这个筛选出的子dataframe是原df的copy还是原df,修改可能会改变原df值,所以报警,修改结果可能会成功。

解决方法:

set的话使用一步搞定,使用loc方法

df.copy方法可以复制出一个新的dataframe来

2.7 Pandas数据排序

操作文件:beijing_tianqi_2018.csv

2.7.1 series排序
#升序
print(df['aqi'].sort_values())
#降序
print(df['aqi'].sort_values(ascending=False))
2.7.2 dataframe排序
单列排序
print(df.sort_values(by='aqi'))
多列排序
#默认升序
print(df.sort_values(by=['aqiLevel','bWendu']))
#一升序一降序
print(df.sort_values(by=['aqiLevel','bWendu'],ascending=[True,False]))

2.8 Pandas字符串处理

series.str

str属性只在series上有,dataframe没有str属性和处理方法;str属性只能在字符串列上使用,不能在数字列上使用;str属性和方法是pandas自己封装的一套方法。

df.loc[:,'bWendu'] = df['bWendu'].str.replace('℃','').astype('int32')
字符串长度
print(df['tianqi'].str.len())
startswith
print(df.index.str.startswith('2018-09'))

返回布尔类型series

需要多次str处理的链式操作

从2018-03-31中提取201803字符串

slice切片方法
print(df.index.str.replace('-','').str.slice(0,6))
#或者
print(df.index.str.replace('-','').str[0:6])
replace默认支持正则表达式
s = df['tianqi'].str.replace("[多阴]","")
print(s)

把多、阴两个汉字去掉:

ymd
2018-01-01    晴~云
2018-01-02     ~云
2018-01-03      云
2018-01-04       
2018-01-05    云~晴
             ... 
2018-12-27    云~晴
2018-12-28      晴
2018-12-29      晴
2018-12-30    晴~云
2018-12-31      云
Name: tianqi, Length: 365, dtype: object

2.9 axis参数

import pandas as pd
import numpy as np

df = pd.DataFrame(
    #arange得到12个数字,然后构建成3行4列的矩阵
    np.arange(12).reshape(3,4),
    columns=['A','B','C','D']
)

print(df)

   A  B   C   D
0  0  1   2   3
1  4  5   6   7
2  8  9  10  11
2.9.1 删除一列
#单列drop,axis=1代表删除某列
df = df.drop('A',axis=1)
print(df)
   B   C   D
0  1   2   3
1  5   6   7
2  9  10  11
2.9.2 删除一行
#单行drop,axis=0代表删除某行
df = df.drop(1,axis=0)
print(df)
   B   C   D
0  1   2   3
2  9  10  11
2.9.3 聚合操作

反直觉,axis参数指定了行/列,代表行/列要动,未被指定的不动。比如指定了axis=行,那聚合的时候就是行从上到下统计,列不变。

求列平均值

print(df)
r = df.mean(axis=0)
print(r)
   A  B   C   D
0  0  1   2   3
1  4  5   6   7
2  8  9  10  11

A    4.0
B    5.0
C    6.0
D    7.0
dtype: float64

df.mean(axis=0)代表跨行输出列结果

输出行结果

r = df.mean(axis=1)
print(r)
0    1.5
1    5.5
2    9.5
dtype: float64
2.9.4 增加一列和(整行的求和)
def get_sum_value(x):
    return x['A'] + x['B'] + x['C'] + x['D']
df['sum_value'] = df.apply(get_sum_value,axis=1)
print(df)
   A  B   C   D  sum_value
0  0  1   2   3          6
1  4  5   6   7         22
2  8  9  10  11         38

2.10 Pandas索引index的用途

  1. 更方便的数据查询
  2. 使用index可以获得性能提升
  3. 自动的数据对其功能
  4. 更多更强大的数据结构支持
import pandas as pd

df = pd.read_csv("./ratings.csv")
print(df.head())
#set_index方法drop=False表示原结构中还保留userId这一行
df.set_index("userId",inplace=True,drop=False)
print(df.head())
   userId  movieId  rating  timestamp
0       1        1     4.0  964982703
1       1        3     4.0  964981247
2       1        6     4.0  964982224
3       1       47     5.0  964983815
4       1       50     5.0  964982931
        userId  movieId  rating  timestamp
userId                                    
1            1        1     4.0  964982703
1            1        3     4.0  964981247
1            1        6     4.0  964982224
1            1       47     5.0  964983815
1            1       50     5.0  964982931

2.10.1 使用index查询

index简化查询

print(df.loc[df['userId'] == 500].head())
#等于
print(df.loc[500].head())
userId                                     
500        500        1     4.0  1005527755
500        500       11     1.0  1005528017
500        500       39     1.0  1005527926
500        500      101     1.0  1005527980
500        500      104     4.0  1005528065
2.10.2 index提升查询性能
  1. 如果index是唯一的,Pandas会使用哈希表优化,查询性能为O(1)
  2. 如果index不是唯一的,但是有序,Pandas会使用二分查找算法,查询性能为O(logN)
  3. 如果index是完全随机的,name每次查询都要扫描全部,查询性能O(N)
判断索引是否单调递增
print(df.index.is_monotonic_increasing)
索引是否唯一
print(df.index.is_unique)
索引排序
df_sorted = df.sort_index()
print(df_sorted.head())
        userId  movieId  rating  timestamp
userId                                    
1            1        1     4.0  964982703
1            1        3     4.0  964981247
1            1        6     4.0  964982224
1            1       47     5.0  964983815
1            1       50     5.0  964982931
使用index能自动对齐数据

包括series和dataframe

s1 = pd.Series([1,2,3],index=list('abc'))
s2 = pd.Series([2,3,4],index=list('bcd'))
print(s1)
print(s2)
print(s1+s2)
a    1
b    2
c    3
dtype: int64
b    2
c    3
d    4
dtype: int64
a    NaN
b    4.0
c    6.0
d    NaN
dtype: float64

Process finished with exit code 0

使用index更多更强大的数据结构支持
  1. CategoricalIndex,基于分类数据的index,提升性能
  2. MultiIndex,多维索引,基于groupby多维聚合后结果
  3. DatetimeIndex,时间类型索引,强大的日期和时间的方法支持

2.11 Dataframe的Merge

merge的语法:

pd.merge(left,right,how='inner',on=None,left_on=None,right_on=None,left_index=False,right_index_False,sort=True,suffixes=('_x','_y'),copy=True,indicator=False,validate=None)

  1. left,right:要merge的dataframe或者有那么的Series
  2. how:join类型,left、right、outer、inner
  3. on:join的key,left和right都需要这个key
  4. left_on:left的df或者series的key(join的列名不一样时使用)
  5. right_on:right的df或者series的key
  6. left_index、right_index:使用index而不是普通的column做join
  7. suffixes:两个元素的后缀,如果列有重名,自动添加后缀,默认是(‘_x’,‘_y’)
2.11.0 读取三个文件:
import pandas as pd
df_ratings = pd.read_csv(
    "./movielens-1m/ratings.dat",
    sep="::",
    engine="python",
    names="UserID::MovieID::Rating::Timestamp".split("::")
)
print(df_ratings.head())

df_users = pd.read_csv(
    "./movielens-1m/users.dat",
    sep="::",
    engine="python",
    names="UserID::Gender::Age::Occupation::Zip-code".split("::")
)
print(df_users.head())

df_movies = pd.read_csv(
    "./movielens-1m/movies.dat",
    sep="::",
    engine="python",
    names="MovieId::Title::Genres".split("::")
)
print(df_movies.head())
   UserID  MovieID  Rating  Timestamp
0       1     1193       5  978300760
1       1      661       3  978302109
2       1      914       3  978301968
3       1     3408       4  978300275
4       1     2355       5  978824291
   UserID Gender  Age  Occupation Zip-code
0       1      F    1          10    48067
1       2      M   56          16    70072
2       3      M   25          15    55117
3       4      M   45           7    02460
4       5      M   25          20    55455
   MovieId                               Title                        Genres
0        1                    Toy Story (1995)   Animation|Children's|Comedy
1        2                      Jumanji (1995)  Adventure|Children's|Fantasy
2        3             Grumpier Old Men (1995)                Comedy|Romance
3        4            Waiting to Exhale (1995)                  Comedy|Drama
4        5  Father of the Bride Part II (1995)                        Comedy

Process finished with exit code 0

2.11.1 merge
df_ratings_users = pd.merge(df_ratings,df_users,left_on="UserID",right_on="UserID",how="inner")
print(df_ratings_users.head())
   UserID  MovieID  Rating  Timestamp Gender  Age  Occupation Zip-code
0       1     1193       5  978300760      F    1          10    48067
1       1      661       3  978302109      F    1          10    48067
2       1      914       3  978301968      F    1          10    48067
3       1     3408       4  978300275      F    1          10    48067
4       1     2355       5  978824291      F    1          10    48067

第二次merge

df_ratings_users_movies = pd.merge(df_ratings_users,df_movies,left_on="MovieID",right_on="MovieId",how="inner")
print(df_ratings_users_movies.head(10))
   UserID  MovieID  ...                                   Title  Genres
0       1     1193  ...  One Flew Over the Cuckoo's Nest (1975)   Drama
1       2     1193  ...  One Flew Over the Cuckoo's Nest (1975)   Drama
2      12     1193  ...  One Flew Over the Cuckoo's Nest (1975)   Drama
3      15     1193  ...  One Flew Over the Cuckoo's Nest (1975)   Drama
4      17     1193  ...  One Flew Over the Cuckoo's Nest (1975)   Drama
5      18     1193  ...  One Flew Over the Cuckoo's Nest (1975)   Drama
6      19     1193  ...  One Flew Over the Cuckoo's Nest (1975)   Drama
7      24     1193  ...  One Flew Over the Cuckoo's Nest (1975)   Drama
8      28     1193  ...  One Flew Over the Cuckoo's Nest (1975)   Drama
9      33     1193  ...  One Flew Over the Cuckoo's Nest (1975)   Drama

[10 rows x 11 columns]

2.11.2 merge时的数量对齐关系
  • one-to-one
    • 结果条数为:1*1
  • one-to-many
    • 结果条数为:1*N,数据以多的一边为准
  • many-to-many
    • 结果条数为:M*N,两边乘积
2.11.3 left join、right join、inner join、outer join的区别

img

2.12 Pandas实现数据合并cancat

使用场景:

批量合并相同格式的Excel、给DataFrame添加行、给Dataframe添加列

2.12.1 语法:
  • 使用某种合并方式(inner/outer)
  • 沿着某个轴向(axis=0/1)
  • 把多个Pandas对象(Dataframe/Series)合并成一个
concat语法:pandas.concat(objs,axis=0,join=‘outer’,ignore_index=False)
  • objs:一个列表,内容可以是dataframe和series,可以混合
  • axis:默认是0代表按行合并,如果等于1代表按列合并
  • join:合并的时候索引的对齐方式,默认是outer jpin,也可以是inner join
  • ignore_index:是否忽略掉原来的数据索引
append语法:Dataframe.append(other,ignore_index=False)

append只有按行合并,没有按列合并,相当于concat按行的简写形式

  • other:单个dataframe、series、dict或者列表
  • ignore_index:是否忽略掉原来的数据索引

1、concat合并

df1 = pd.DataFrame({
    'A':['A0','A1','A2','A3'],
    'B':['B0','B1','B2','B3'],
    'C':['C0','C1','C2','C3'],
    'D':['D0','D1','D2','D3'],
    'E':['E0','E1','E2','E3']
})

df2 = pd.DataFrame({
    'A':['A4','A5','A6','A7'],
    'B':['B4','B5','B6','B7'],
    'C':['C4','C5','C6','C7'],
    'D':['D4','D5','D6','D7'],
    'F':['F4','F5','F6','F7']
})
print(df1)
print(df2)
df3 = pd.concat([df1,df2])
print(df3)
    A   B   C   D   E
0  A0  B0  C0  D0  E0
1  A1  B1  C1  D1  E1
2  A2  B2  C2  D2  E2
3  A3  B3  C3  D3  E3
    A   B   C   D   F
0  A4  B4  C4  D4  F4
1  A5  B5  C5  D5  F5
2  A6  B6  C6  D6  F6
3  A7  B7  C7  D7  F7
    A   B   C   D    E    F
0  A0  B0  C0  D0   E0  NaN
1  A1  B1  C1  D1   E1  NaN
2  A2  B2  C2  D2   E2  NaN
3  A3  B3  C3  D3   E3  NaN
0  A4  B4  C4  D4  NaN   F4
1  A5  B5  C5  D5  NaN   F5
2  A6  B6  C6  D6  NaN   F6
3  A7  B7  C7  D7  NaN   F7

2、忽略原来索引:

df3 = pd.concat([df1,df2],ignore_index=True)
print(df3)
    A   B   C   D    E    F
0  A0  B0  C0  D0   E0  NaN
1  A1  B1  C1  D1   E1  NaN
2  A2  B2  C2  D2   E2  NaN
3  A3  B3  C3  D3   E3  NaN
4  A4  B4  C4  D4  NaN   F4
5  A5  B5  C5  D5  NaN   F5
6  A6  B6  C6  D6  NaN   F6
7  A7  B7  C7  D7  NaN   F7

3、join=inner过滤掉不匹配的列

df3 = pd.concat([df1,df2],ignore_index=True,join="inner")
print(df3)
    A   B   C   D
0  A0  B0  C0  D0
1  A1  B1  C1  D1
2  A2  B2  C2  D2
3  A3  B3  C3  D3
4  A4  B4  C4  D4
5  A5  B5  C5  D5
6  A6  B6  C6  D6
7  A7  B7  C7  D7

4、使用axis=1添加新列

print(df1)
s1 = pd.Series(list(range(4)),name="F")
dff = pd.concat([df1,s1],axis=1)
print(dff)
    A   B   C   D   E
0  A0  B0  C0  D0  E0
1  A1  B1  C1  D1  E1
2  A2  B2  C2  D2  E2
3  A3  B3  C3  D3  E3
    A   B   C   D   E  F
0  A0  B0  C0  D0  E0  0
1  A1  B1  C1  D1  E1  1
2  A2  B2  C2  D2  E2  2
3  A3  B3  C3  D3  E3  3

4.1、添加多列series

s2 = df1.apply(lambda x:x["A"]+"_GG",axis=1)
print(s2)
s2.name = 'G'
dff = pd.concat([df1,s1,s2],axis=1)
print(dff)
    A   B   C   D   E  F      G
0  A0  B0  C0  D0  E0  0  A0_GG
1  A1  B1  C1  D1  E1  1  A1_GG
2  A2  B2  C2  D2  E2  2  A2_GG
3  A3  B3  C3  D3  E3  3  A3_GG

列表中可以只有series,也可以dataframe、series混合

dff = pd.concat([s1,s2],axis=1)
print(dff)

dff = pd.concat([s1,df1,s2],axis=1)
print(dff)
   F   A   B   C   D   E      G
0  0  A0  B0  C0  D0  E0  A0_GG
1  1  A1  B1  C1  D1  E1  A1_GG
2  2  A2  B2  C2  D2  E2  A2_GG
3  3  A3  B3  C3  D3  E3  A3_GG
2.12.2 append语法(后续会取消函数,推荐使用concat)
df1 = pd.DataFrame([[1,2],[3,4]],columns=list('AB'))
df2 = pd.DataFrame([[5,6],[7,8]],columns=list('AB'))
print(df1)
print(df2)
print(df1.append(df2,ignore_index=True))
   A  B
0  1  2
1  3  4
   A  B
0  5  6
1  7  8
   A  B
0  1  2
1  3  4
2  5  6
3  7  8

一行一行的给dataframe添加数据

dff = pd.concat(
    [pd.DataFrame([i],columns=['A']) for i in range(5)],
    ignore_index=True
)
print(dff)
   A
0  0
1  1
2  2
3  3
4  4

2.13 Pandas批量拆分和合并Excel

work_dir = "./c15_excel_split_merge"
splits_dir = f"{work_dir}/splits"
import os
if not os.path.exists(splits_dir):
    os.mkdir(splits_dir)

import pandas as pd
df_source = pd.read_excel("./c15_excel_split_merge/crazyant_blog_articles_source.xlsx")
print(df_source.head())
print(df_source.index)
#返回行、列
print(df_source.shape)
total_row = df_source.shape[0]
print(total_row)
     id                 title                    tags
0  2585  Tensorflow怎样接收变长列表特征  python,tensorflow,特征工程
1  2583   Pandas实现数据的合并concat      pandas,python,数据分析
2  2574  Pandas的Index索引有什么用途?      pandas,python,数据分析
3  2564           机器学习常用数据集大全             python,机器学习
4  2561          一个数据科学家的修炼路径                    数据分析
RangeIndex(start=0, stop=258, step=1)
(258, 3)
258
2.13.1 将一个大Excel拆成多个Excel
  1. 使用df.iloc方法,将一个打的dataframe差分成多个小的dataframe:iloc使用数字索引切片
  2. 使用dataframe.to_excel保存每个小Excel

#将大Excel拆分给这几个人
user_names = ["xiao_shuai","xiao_wang","xiao_ming","xiao_lei","xiao_bo","xiao_hong"]
#计算每个人的任务数
#“//”运算符,取整除,返回商的整数部分(向下取整)
split_size = total_row // len(user_names)
if total_row % len(user_names):
    split_size += 1
df_subs = []
for idx,user_name in enumerate(user_names):
    #iloc开始、结束索引
    begin = idx*split_size
    end = begin+split_size
    #df按照iloc拆分
    df_sub = df_source.iloc[begin:end]
    #df_subs存入一个元组
    df_subs.append((idx,user_name,df_sub))

#将子dataframe存入Excel
for idx,user_name,df_sub in df_subs:
    file_name = f"{splits_dir}/task_{idx}_{user_name}.xlsx"
    df_sub.to_excel(file_name,index=False)
2.13.2 合并多个小Excel到一个大Excel
  1. 遍历文件夹,得到要合并的Excel文件列表
  2. 分别读取到dataframe,给每个df添加一列标记来源
  3. 使用pd.concat进行df批量合并
  4. 将合并后的dataframe输出到Excel

#列出Excel名称列表
excel_names = []
for excel_name in os.listdir(splits_dir):
    excel_names.append(excel_name)
print(excel_names)
#读取到dataframe
df_list = []
for excel_name in excel_names:
    excel_path = f"{splits_dir}/{excel_name}"
    df_split = pd.read_excel(excel_path)
    #解析username
    user_name = excel_name.replace("task_","").replace(".xlsx","")[2:]
    #给每个df加一列用户名称
    df_split["username"] = user_name
    df_list.append(df_split)
#df.concat合并
df_merge = pd.concat(df_list)
print(df_merge.shape)
print(df_merge.head())
df_merge.to_excel(f"{work_dir}/task_merge.xlsx",index=False)
(258, 4)
     id                 title                    tags    username
0  2585  Tensorflow怎样接收变长列表特征  python,tensorflow,特征工程  xiao_shuai
1  2583   Pandas实现数据的合并concat      pandas,python,数据分析  xiao_shuai
2  2574  Pandas的Index索引有什么用途?      pandas,python,数据分析  xiao_shuai
3  2564           机器学习常用数据集大全             python,机器学习  xiao_shuai
4  2561          一个数据科学家的修炼路径                    数据分析  xiao_shuai

2.14 groupby分组统计

2.14.1 单列groupby
import pandas as pd
import numpy as np
#加上这行,能在jupyter notebook展示matplot图表
%matplotlib inline
#使用方式 data.plot()
df = pd.DataFrame({
    'A':['foo','bar','foo','bar','foo','bar','foo','foo'],
    'B':['one','one','two','three','two','two','one','three'],
    'C':np.random.randn(8),
    'D':np.random.randn(8)
})
print(df)

df1 = df.groupby('A').sum()
print(df1)

groupby的A列变成了数据的索引列;sum统计数字,B是字符所以被忽略。

     A      B         C         D
0  foo    one -1.106856  0.713179
1  bar    one  1.273161  0.342217
2  foo    two -0.378437  3.338582
3  bar  three -0.634966 -0.153653
4  foo    two  0.044327 -0.189306
5  bar    two  0.543985 -0.174611
6  foo    one  0.352282  1.066336
7  foo  three  1.231313  1.395095
            C         D
A                      
bar  1.182180  0.013953
foo  0.142628  6.323886
2.14.2 多列groupby
df1 = df.groupby(['A','B']).mean()
print(df1)

变成A B二级索引

                  C         D
A   B                        
bar one   -0.520902  1.026888
    three  0.607355  0.119211
    two   -1.670414 -0.730202
foo one   -0.038131  1.442606
    three -0.925947 -0.901537
    two   -0.611745 -0.216459

df1 = df.groupby(['A','B'],as_index=False).mean()
print(df1)
     A      B         C         D
0  bar    one -1.801801 -0.353210
1  bar  three -0.626959  1.805818
2  bar    two -0.420621  1.374015
3  foo    one  0.020856  0.557639
4  foo  three -0.500431 -0.774059
5  foo    two -0.541895 -0.177716
2.14.3 同时查看多种数据统计
df1 = df.groupby('A').agg([np.sum,np.mean,np.std])
print(df1)

列变成了多级索引

            C                             D                    
          sum      mean       std       sum      mean       std
A                                                              
bar  1.257328  0.419109  1.255378 -2.147189 -0.715730  0.901087
foo -2.009106 -0.401821  0.372771  1.737854  0.347571  0.638935
2.14.4 查看单列的结果数据统计
df1 = df.groupby('A')['C'].agg([np.sum,np.mean,np.std])
print(df1)
          sum      mean       std
A                                
bar -0.494485 -0.164828  0.830111
foo -0.787843 -0.157569  0.728112
2.14.5 不同列使用不同聚合函数
df1 = df.groupby('A').agg({"C":np.sum,"D":np.mean})
print(df1)
            C         D
A                      
bar  1.717162 -0.530213
foo  0.463014 -0.295544
2.14.6 遍历groupby对象
g = df.groupby('A')
for name,group in g:
    print(name)
    print(group)
    print()
bar
     A      B         C         D
1  bar    one  1.691832  0.537865
3  bar  three -0.581042  0.760365
5  bar    two  1.162899  1.119155

foo
     A      B         C         D
0  foo    one  0.211515 -0.009519
2  foo    two  0.028462  1.434644
4  foo    two -3.367568  1.996146
6  foo    one -0.950889  0.573215
7  foo  three  0.693937  0.408178
print(g.get_group('bar'))
     A      B         C         D
1  bar    one -1.051277 -0.996631
3  bar  three -1.298129 -2.053288
5  bar    two -0.658050  1.274031
2.14.7 遍历多个列聚合分组
g = df.groupby(['A','B'])
for name,group in g:
    print(name)
    print(group)
    print()

name是两个元素的tuple

('bar', 'one')
     A    B        C         D
1  bar  one -1.13261 -1.871892

('bar', 'three')
     A      B        C         D
3  bar  three  0.45064  0.443138

('bar', 'two')
     A    B         C         D
5  bar  two  0.024066 -0.326454

('foo', 'one')
     A    B         C         D
0  foo  one  1.241679  1.526465
6  foo  one  1.535248 -1.096181

('foo', 'three')
     A      B         C         D
7  foo  three  0.745874 -0.630024

('foo', 'two')
     A    B         C         D
2  foo  two -0.033113 -0.058216
4  foo  two -0.116838  1.180435
print(g.get_group(('foo','one')))
     A    B         C         D
0  foo  one -1.294620  2.204648
6  foo  one  2.306578  0.370950

直接查询group后的某几个列,生成Series或字Dataframe

print(g['C'])
for name,group in g['C']:
    print(name)
    print(group)
    print(type(group))
    print()
<pandas.core.groupby.generic.SeriesGroupBy object at 0x0000021DF8E5CD30>
('bar', 'one')
1    0.147317
Name: C, dtype: float64
<class 'pandas.core.series.Series'>

('bar', 'three')
3   -1.817367
Name: C, dtype: float64
<class 'pandas.core.series.Series'>

('bar', 'two')
5    0.861903
Name: C, dtype: float64
<class 'pandas.core.series.Series'>

('foo', 'one')
0   -0.097629
6   -1.862348
Name: C, dtype: float64
<class 'pandas.core.series.Series'>

('foo', 'three')
7    0.365501
Name: C, dtype: float64
<class 'pandas.core.series.Series'>

('foo', 'two')
2    1.713429
4   -1.001633
Name: C, dtype: float64
<class 'pandas.core.series.Series'>
2.14.8 分组探索天气数据
df = pd.read_csv('./beijing_tianqi_2018.csv')
df.loc[:,'bWendu'] = df['bWendu'].str.replace('℃','').astype('int32')
df.loc[:,'yWendu'] = df['yWendu'].str.replace('℃','').astype('int32')
print(df.head())
#新增一列月份
df['month'] = df['ymd'].str[:7]
print(df.head())
          ymd  bWendu  yWendu tianqi fengxiang fengli  aqi aqiInfo  aqiLevel
0  2018-01-01       3      -6   晴~多云       东北风   1-2级   59       良         2
1  2018-01-02       2      -5   阴~多云       东北风   1-2级   49       优         1
2  2018-01-03       2      -5     多云        北风   1-2级   28       优         1
3  2018-01-04       0      -8      阴       东北风   1-2级   28       优         1
4  2018-01-05       3      -6   多云~晴       西北风   1-2级   50       优         1
          ymd  bWendu  yWendu tianqi  ... aqi aqiInfo  aqiLevel    month
0  2018-01-01       3      -6   晴~多云  ...  59       良         2  2018-01
1  2018-01-02       2      -5   阴~多云  ...  49       优         1  2018-01
2  2018-01-03       2      -5     多云  ...  28       优         1  2018-01
3  2018-01-04       0      -8      阴  ...  28       优         1  2018-01
4  2018-01-05       3      -6   多云~晴  ...  50       优         1  2018-01

[5 rows x 10 columns]

2.14.8.1 查看每个月最高温
data = df.groupby('month')['bWendu'].max()
print(data)
month
2018-01     7
2018-02    12
2018-03    27
2018-04    30
2018-05    35
2018-06    38
2018-07    37
2018-08    36
2018-09    31
2018-10    25
2018-11    18
2018-12    10
Name: bWendu, dtype: int32

2.14.8.2 查看每个月最高温、最低温度、平均空气质量
import numpy as np
group_data = df.groupby('month').agg({"bWendu":np.max,"yWendu":np.min,"aqi":np.mean})
print(group_data)

         bWendu  yWendu         aqi
month                              
2018-01       7     -12   60.677419
2018-02      12     -10   78.857143
2018-03      27      -4  130.322581
2018-04      30       1  102.866667
2018-05      35      10   99.064516
2018-06      38      17   82.300000
2018-07      37      22   72.677419
2018-08      36      20   59.516129
2018-09      31      11   50.433333
2018-10      25       1   67.096774
2018-11      18      -4  105.100000
2018-12      10     -12   77.354839

2.15 Pandas的分层索引MultiIndex

import pandas as pd

stocks = pd.read_excel("./stocks/互联网公司股票.xlsx")
print(stocks.shape)
print(stocks.head(3))
#查看都有哪些公司
print(stocks['公司'].unique())
#查看收盘时平均值
print(stocks.groupby('公司')['收盘'].mean())
(12, 8)
           日期    公司      收盘      开盘       高       低   交易量   涨跌幅
0  2019-10-03  BIDU  104.32  102.35  104.73  101.15  2.24  0.02
1  2019-10-02  BIDU  102.62  100.85  103.24   99.50  2.69  0.01
2  2019-10-01  BIDU  102.00  102.80  103.26  101.00  1.78 -0.01
['BIDU' 'BABA' 'IQ' 'JD']
公司
BABA    166.80
BIDU    102.98
IQ       15.90
JD       28.35
Name: 收盘, dtype: float64
2.15.1 Series的分层索引MultiIndex
ser = stocks.groupby(["公司","日期"])["收盘"].mean()
print(ser)
print(ser.index)
公司    日期        
BABA  2019-10-01    165.15
      2019-10-02    165.77
      2019-10-03    169.48
BIDU  2019-10-01    102.00
      2019-10-02    102.62
      2019-10-03    104.32
IQ    2019-10-01     15.92
      2019-10-02     15.72
      2019-10-03     16.06
JD    2019-10-01     28.19
      2019-10-02     28.06
      2019-10-03     28.80
Name: 收盘, dtype: float64

MultiIndex([('BABA', '2019-10-01'),
            ('BABA', '2019-10-02'),
            ('BABA', '2019-10-03'),
            ('BIDU', '2019-10-01'),
            ('BIDU', '2019-10-02'),
            ('BIDU', '2019-10-03'),
            (  'IQ', '2019-10-01'),
            (  'IQ', '2019-10-02'),
            (  'IQ', '2019-10-03'),
            (  'JD', '2019-10-01'),
            (  'JD', '2019-10-02'),
            (  'JD', '2019-10-03')],
           names=['公司', '日期'])

Process finished with exit code 0

#unstack把二级索引变成行
print(ser.unstack())
#把索引变成普通的列
print(ser.reset_index())
日期    2019-10-01  2019-10-02  2019-10-03
公司                                      
BABA      165.15      165.77      169.48
BIDU      102.00      102.62      104.32
IQ         15.92       15.72       16.06
JD         28.19       28.06       28.80

      公司          日期      收盘
0   BABA  2019-10-01  165.15
1   BABA  2019-10-02  165.77
2   BABA  2019-10-03  169.48
3   BIDU  2019-10-01  102.00
4   BIDU  2019-10-02  102.62
5   BIDU  2019-10-03  104.32
6     IQ  2019-10-01   15.92
7     IQ  2019-10-02   15.72
8     IQ  2019-10-03   16.06
9     JD  2019-10-01   28.19
10    JD  2019-10-02   28.06
11    JD  2019-10-03   28.80
2.15.2 Series的多级索引MultiIndex如何筛选数据
print(ser.loc['BIDU'])
#使用元组定位到多重索引
print(ser.loc[('BIDU','2019-10-02')])
#逗号前第一个索引,逗号后是第二个索引
print(ser.loc[:,'2019-10-02'])
日期
2019-10-01    102.00
2019-10-02    102.62
2019-10-03    104.32
Name: 收盘, dtype: float64

102.62

公司
BABA    165.77
BIDU    102.62
IQ       15.72
JD       28.06
Name: 收盘, dtype: float64
2.15.3 Dataframe的多层索引MultiIndex
#设置二级索引
stocks.set_index(['公司','日期'],inplace=True)
print(stocks.head())
print(stocks.index)
#索引排序
stocks.sort_index(inplace=True)
print(stocks)
                     收盘      开盘       高       低    交易量   涨跌幅
公司   日期                                                     
BIDU 2019-10-03  104.32  102.35  104.73  101.15   2.24  0.02
     2019-10-02  102.62  100.85  103.24   99.50   2.69  0.01
     2019-10-01  102.00  102.80  103.26  101.00   1.78 -0.01
BABA 2019-10-03  169.48  166.65  170.18  165.00  10.39  0.02
     2019-10-02  165.77  162.82  166.88  161.90  11.60  0.00
MultiIndex([('BIDU', '2019-10-03'),
            ('BIDU', '2019-10-02'),
            ('BIDU', '2019-10-01'),
            ('BABA', '2019-10-03'),
            ('BABA', '2019-10-02'),
            ('BABA', '2019-10-01'),
            (  'IQ', '2019-10-03'),
            (  'IQ', '2019-10-02'),
            (  'IQ', '2019-10-01'),
            (  'JD', '2019-10-03'),
            (  'JD', '2019-10-02'),
            (  'JD', '2019-10-01')],
           names=['公司', '日期'])
                     收盘      开盘       高       低    交易量   涨跌幅
公司   日期                                                     
BABA 2019-10-01  165.15  168.01  168.23  163.64  14.19 -0.01
     2019-10-02  165.77  162.82  166.88  161.90  11.60  0.00
     2019-10-03  169.48  166.65  170.18  165.00  10.39  0.02
BIDU 2019-10-01  102.00  102.80  103.26  101.00   1.78 -0.01
     2019-10-02  102.62  100.85  103.24   99.50   2.69  0.01
     2019-10-03  104.32  102.35  104.73  101.15   2.24  0.02
IQ   2019-10-01   15.92   16.14   16.22   15.50  11.65 -0.01
     2019-10-02   15.72   15.85   15.87   15.12   8.10 -0.01
     2019-10-03   16.06   15.71   16.38   15.32  10.08  0.02
JD   2019-10-01   28.19   28.22   28.57   27.97  10.64  0.00
     2019-10-02   28.06   28.00   28.22   27.53   9.53  0.00
     2019-10-03   28.80   28.11   28.97   27.82   8.77  0.03
2.15.4 Dataframe的多层索引MultiIndex如何筛选数据

在选择数据时

  1. 元组(key1,key2)代表筛选多层索引,其中key1是索引第一级,key2是索引第二级,比如key1=JD,key2=2019-10-02
  2. 列表[key1,key2]代表同一层的多个key,其中key1和key2是并列的同级索引,比如key1=JD,key2=BIDU
print(stocks.loc['BIDU'])

print(stocks.loc[('BIDU','2019-10-02'),:])

print(stocks.loc[('BIDU','2019-10-02'),'开盘'])
#BIDU和JD的03日所有信息dataframe
print(stocks.loc[(['BIDU','JD'],'2019-10-03'),:])

print(stocks.loc[(['BIDU','JD'],'2019-10-03'),'收盘'])

#BIDU两天的收盘价
print(stocks.loc[('BIDU',['2019-10-02','2019-10-03']),'收盘'])

结果:

                收盘      开盘       高       低   交易量   涨跌幅
日期                                                    
2019-10-01  102.00  102.80  103.26  101.00  1.78 -0.01
2019-10-02  102.62  100.85  103.24   99.50  2.69  0.01
2019-10-03  104.32  102.35  104.73  101.15  2.24  0.02

收盘     102.62
开盘     100.85
高      103.24
低       99.50
交易量      2.69
涨跌幅      0.01
Name: (BIDU, 2019-10-02), dtype: float64

100.85

                     收盘      开盘       高       低   交易量   涨跌幅
公司   日期                                                    
BIDU 2019-10-03  104.32  102.35  104.73  101.15  2.24  0.02
JD   2019-10-03   28.80   28.11   28.97   27.82  8.77  0.03

公司    日期        
BIDU  2019-10-03    104.32
JD    2019-10-03     28.80
Name: 收盘, dtype: float64

公司    日期        
BIDU  2019-10-02    102.62
      2019-10-03    104.32
Name: 收盘, dtype: float64

slice(None)代表筛选这一索引的所有内容

print(stocks.loc[(slice(None),['2019-10-02','2019-10-03']),:])
#把索引变成普通的列
print(stock.reset_index())
                     收盘      开盘       高       低    交易量   涨跌幅
公司   日期                                                     
BABA 2019-10-02  165.77  162.82  166.88  161.90  11.60  0.00
     2019-10-03  169.48  166.65  170.18  165.00  10.39  0.02
BIDU 2019-10-02  102.62  100.85  103.24   99.50   2.69  0.01
     2019-10-03  104.32  102.35  104.73  101.15   2.24  0.02
IQ   2019-10-02   15.72   15.85   15.87   15.12   8.10 -0.01
     2019-10-03   16.06   15.71   16.38   15.32  10.08  0.02
JD   2019-10-02   28.06   28.00   28.22   27.53   9.53  0.00
     2019-10-03   28.80   28.11   28.97   27.82   8.77  0.03

2.16 数据转换函数map、apply、applymap

  1. map:只用于Series,实现每个值-》值得映射
  2. apply:用于Series实现每个值的处理,用于Dataframe实现某个轴的Series处理
  3. applymap:只能用于Dataframe,用于处理该Dataframe的每个元素
2.16.1 map用于Series值的转换
stocks = pd.read_excel("./stocks/互联网公司股票.xlsx")
print(stocks.head(3))
print(stocks['公司'].unique())
#小写公司拼音
dict_company_names = {
    "bidu":"百度",
    "baba":"阿里巴巴",
    "iq":"爱奇艺",
    "jd":"京东"
}
           日期    公司      收盘      开盘       高       低   交易量   涨跌幅
0  2019-10-03  BIDU  104.32  102.35  104.73  101.15  2.24  0.02
1  2019-10-02  BIDU  102.62  100.85  103.24   99.50  2.69  0.01
2  2019-10-01  BIDU  102.00  102.80  103.26  101.00  1.78 -0.01
['BIDU' 'BABA' 'IQ' 'JD']
2.16.1.1 Series.map(dict)

series.map(dict) or series.map(function)均可

df['gender'] = df['gender'].map({"mele":1,"female":2})
stocks['公司中文1'] = stocks["公司"].str.lower().map(dict_company_names)
print(stocks.head())
           日期    公司      收盘      开盘       高       低    交易量   涨跌幅 公司中文1
0  2019-10-03  BIDU  104.32  102.35  104.73  101.15   2.24  0.02    百度
1  2019-10-02  BIDU  102.62  100.85  103.24   99.50   2.69  0.01    百度
2  2019-10-01  BIDU  102.00  102.80  103.26  101.00   1.78 -0.01    百度
3  2019-10-03  BABA  169.48  166.65  170.18  165.00  10.39  0.02  阿里巴巴
4  2019-10-02  BABA  165.77  162.82  166.88  161.90  11.60  0.00  阿里巴巴
2.16.1.1 Series.map(function)
stocks['公司中文2'] = stocks["公司"].map(lambda x:dict_company_names[x.lower()])
print(stocks.head())
           日期    公司      收盘      开盘       高       低    交易量   涨跌幅 公司中文1 公司中文2
0  2019-10-03  BIDU  104.32  102.35  104.73  101.15   2.24  0.02    百度    百度
1  2019-10-02  BIDU  102.62  100.85  103.24   99.50   2.69  0.01    百度    百度
2  2019-10-01  BIDU  102.00  102.80  103.26  101.00   1.78 -0.01    百度    百度
3  2019-10-03  BABA  169.48  166.65  170.18  165.00  10.39  0.02  阿里巴巴  阿里巴巴
4  2019-10-02  BABA  165.77  162.82  166.88  161.90  11.60  0.00  阿里巴巴  阿里巴巴
2.16.2 apply函数用于Series绝DataFrame的转换
  • Series.apply(function),函数的参数是每个值
  • DataFrame.apply(function),函数的参数是Series
#function的参数是每个Series里的值
stocks['公司中文3'] = stocks["公司"].apply(lambda x:dict_company_names[x.lower()])

#function的参数是对应轴的Series,axis=1,所以Series的key是列名,可以用x['公司']获取
stocks['公司中文4'] = stocks.apply(lambda x:dict_company_names[x['公司'].lower()],axis=1)
print(stocks.head())
           日期    公司      收盘      开盘       高  ...   涨跌幅  公司中文1  公司中文2 公司中文3 公司中文4
0  2019-10-03  BIDU  104.32  102.35  104.73  ...  0.02     百度     百度    百度    百度
1  2019-10-02  BIDU  102.62  100.85  103.24  ...  0.01     百度     百度    百度    百度
2  2019-10-01  BIDU  102.00  102.80  103.26  ... -0.01     百度     百度    百度    百度
3  2019-10-03  BABA  169.48  166.65  170.18  ...  0.02   阿里巴巴   阿里巴巴  阿里巴巴  阿里巴巴
4  2019-10-02  BABA  165.77  162.82  166.88  ...  0.00   阿里巴巴   阿里巴巴  阿里巴巴  阿里巴巴
2.16.3 applymap函数用于DataFrame所有值的转换
sub_df = stocks[['收盘','开盘','高','低','交易量']]
print(sub_df.head())
#将所有小数转换成整数,x是每个值
sub_df = sub_df.applymap(lambda x:int(x))
print(sub_df.head())
stocks.loc[:,['收盘','开盘','高','低','交易量']] = sub_df
print(stocks.head())
           日期    公司   收盘   开盘    高    低  交易量   涨跌幅 公司中文1 公司中文2 公司中文3 公司中文4
0  2019-10-03  BIDU  104  102  104  101            2  0.02    百度    百度    百度    百度
1  2019-10-02  BIDU  102  100  103   99            2  0.01    百度    百度    百度    百度
2  2019-10-01  BIDU  102  102  103  101            1 -0.01    百度    百度    百度    百度
3  2019-10-03  BABA  169  166  170  165           10  0.02  阿里巴巴  阿里巴巴  阿里巴巴  阿里巴巴
4  2019-10-02  BABA  165  162  166  161           11  0.00  阿里巴巴  阿里巴巴  阿里巴巴  阿里巴巴

2.17 Pandas对每个分组应用apply函数

pandas的groupby遵从split、apply、combine模式

split指pandas的groupby,我们自己实现apply函数,apply返回的结果由pandas进行combine得到结果。

GroupBy.apply(function)

  1. function的第一个参数是dataframe
  2. function返回结果,可以是dataframe、series、单个值、设置和输入dataframe完全没有关系
2.17.1 对数值列按分组归一化

将不同范围的数值映射到[0,1]区间

公式:(x-xmin)/(xmax-xmin)

用户对电影评分的归一化

df_ratings = pd.read_csv(
    "./movielens-1m/ratings.dat",
    sep="::",
    engine="python",
    names="UserID::MovieID::Rating::Timestamp".split("::")
)
print(df_ratings.head())
def ratings_norm(df):
    """
    df是每个用户分组的df
    :param df:
    :return:
    """
    min_value = df["Rating"].min()
    max_value = df["Rating"].max()
    df["Rating_norm"] = df["Rating"].apply(lambda x:(x-min_value)/(max_value-min_value))
    return df
df_ratings = df_ratings.groupby("UserID").apply(ratings_norm)
print(df_ratings.loc[df_ratings['UserID'] == 1,:].head())
#简写
#print(df_ratings[df_ratings['UserID'] == 1].head())
   UserID  MovieID  Rating  Timestamp
0       1     1193       5  978300760
1       1      661       3  978302109
2       1      914       3  978301968
3       1     3408       4  978300275
4       1     2355       5  978824291

   UserID  MovieID  Rating  Timestamp  Rating_norm
0       1     1193       5  978300760          1.0
1       1      661       3  978302109          0.0
2       1      914       3  978301968          0.0
3       1     3408       4  978300275          0.5
4       1     2355       5  978824291          1.0
2.17.2 取每个分组的TopN数据
df = pd.read_csv('./beijing_tianqi_2018.csv')
df.loc[:,'bWendu'] = df['bWendu'].str.replace('℃','').astype('int32')
df.loc[:,'yWendu'] = df['yWendu'].str.replace('℃','').astype('int32')
#新增月份
df['month'] = df['ymd'].str[:7]
print(df.head())
          ymd  bWendu  yWendu tianqi  ... aqi aqiInfo  aqiLevel    month
0  2018-01-01       3      -6   晴~多云  ...  59       良         2  2018-01
1  2018-01-02       2      -5   阴~多云  ...  49       优         1  2018-01
2  2018-01-03       2      -5     多云  ...  28       优         1  2018-01
3  2018-01-04       0      -8      阴  ...  28       优         1  2018-01
4  2018-01-05       3      -6   多云~晴  ...  50       优         1  2018-01

[5 rows x 10 columns]

按month分组,取温度最高的几天

#按month分组,取温度最高的几天
def getWenduTopN(df,topn):
    """
    df是按月份分组后的df
    :return:
    """
    #用bWendu排序,只取两列,然后切片语法取topn行
    return df.sort_values(by="bWendu")[["ymd","bWendu"]][-topn:]
#apply中使用kv形式传入其他参数,比如topn
df1 = df.groupby('month').apply(getWenduTopN,topn=2)
print(df1.head())
                   ymd  bWendu
month                         
2018-01 13  2018-01-14       6
        18  2018-01-19       7
2018-02 53  2018-02-23      10
        56  2018-02-26      12
2018-03 86  2018-03-28      25

2.18 Pandas的stack和pivot实现数据透视

将二维数据变成二维交叉形式,便于分析,叫做重塑或透视

2.18.1通过统计得到多维度指标数据

统计得到电影评分数据集,每个月份每个分数被评分多少次:月份、分数1-5、次数

import numpy as np
df = pd.read_csv(
    "./movielens-1m/ratings.dat",
    sep="::",
    engine="python",
    names="UserID::MovieID::Rating::Timestamp".split("::"),
    header=None
)
#转成时间格式可以使用pandas时间戳本身对象的方法
df["pdate"] = pd.to_datetime(df["Timestamp"],unit='s')
print(df.head())
print(df.dtypes)
#两列groupby,第一列是日期格式前的月份部分,第二列评分
df_group = df.groupby([df['pdate'].dt.month,'Rating'])['UserID'].agg(pv=np.sum)
print(df_group.head(10))
   UserID  MovieID  Rating  Timestamp               pdate
0       1     1193       5  978300760 2000-12-31 22:12:40
1       1      661       3  978302109 2000-12-31 22:35:09
2       1      914       3  978301968 2000-12-31 22:32:48
3       1     3408       4  978300275 2000-12-31 22:04:35
4       1     2355       5  978824291 2001-01-06 23:38:11
UserID                int64
MovieID               int64
Rating                int64
Timestamp             int64
pdate        datetime64[ns]
dtype: object
                    pv
pdate Rating          
1     1        2613452
      2        5294359
      3       12273331
      4       16452340
      5        9580889
2     1        1608795
      2        3747792
      3        8608216
      4       10941690
      5        5819374
2.18.2 使用unstack实现数据二维透视
#将rating和pdate两个索引转成行+列的索引方式的二维数据
df_stack = df_group.unstack()
print(df_stack)
#使用jupyter notebook引用 %matplotlib inline 后可以展示图表
df_stack.plot()
#和unstack是互逆操作
print(df_stack.stack().head(10))
              pv                                           
Rating         1         2          3          4          5
pdate                                                      
1        2613452   5294359   12273331   16452340    9580889
2        1608795   3747792    8608216   10941690    5819374
3        1344378   2772172    6156512    6911661    3228406
4        4397883  10147641   25330546   30406281   18070625
5       24893943  41021601   99671422  138699142   98385825
6       15896973  31940801   75297592  109067096   72816021
7       22506252  44079974  118016191  163188814  104777329
8       42290096  80215303  196812819  250299153  165391463
9       10185866  19751997   49514561   66638484   43733790
10       6825621  14936083   37937741   49413212   31949765
11      29762500  55254306  133879416  181288004  118358977
12       5474475  11043533   25870578   33351106   19000303
AxesSubplot(0.125,0.11;0.775x0.77)

                    pv
pdate Rating          
1     1        2613452
      2        5294359
      3       12273331
      4       16452340
      5        9580889
2     1        1608795
      2        3747792
      3        8608216
      4       10941690
      5        5819374

2.18.3 使用pivot简化透视

pivot方法相当于对df使用set_index创建分层索引,然后调用unstack

df_reset = df_group.reset_index()
print(df_reset.head())
#参数就是 y轴(pdate) x轴(Rating) 和值(pv)
df_pivot = df_reset.pivot('pdate','Rating','pv')
print(df_pivot.head())

df_pivot.plot()
   pdate  Rating        pv
0      1       1   2613452
1      1       2   5294359
2      1       3  12273331
3      1       4  16452340
4      1       5   9580889
Rating         1         2         3          4         5
pdate                                                    
1        2613452   5294359  12273331   16452340   9580889
2        1608795   3747792   8608216   10941690   5819374
3        1344378   2772172   6156512    6911661   3228406
4        4397883  10147641  25330546   30406281  18070625
5       24893943  41021601  99671422  138699142  98385825
2.18.4 stack、unstack、pivot的语法
stack:DataFrame.stack(level=-1,dropna=True),将column变成index,类似把横放的书籍变成竖放

level=-1代表多层索引的最内层,可以通过==0/1/2指定多层索引的对应层

unstack:df.unstack(level=-1,fill_value=None),将index变成column。类似把竖放的书籍横放
pivot:df.pivot(index=None,columns=None,valuies=None),指定index、columns、values实现二维透视

2.19 pandas日期数据处理

pandas可以将各种格式的日期数据转为pandas的日期对象,使用对象函数处理

  1. pd.to_datetime:将字符串、列表、series变成日期类型
  2. Timestamp:pandas表示日期的对象形式
  3. DatetimeIndex:pandas表示日期的对象列表形式
  • DatetimeIndex是Timestamp的列表形式
  • pd.to_datetime对单个日期字符串处理会得到TImestamp
  • pd.to_datetime对日期字符串列表处理会得到DatetimeIndex
# 小时
df["time"].dt.hour
# 月份
df["time"],dt.month
2.19.1 读取天气数据,将日期列转换成pandas的日期
df = pd.read_csv('./beijing_tianqi_2018.csv')
df.loc[:,'bWendu'] = df['bWendu'].str.replace('℃','').astype('int32')
df.loc[:,'yWendu'] = df['yWendu'].str.replace('℃','').astype('int32')
df.set_index(pd.to_datetime(df["ymd"]),inplace=True)
print(df.head())
print(df.index)

datetime64[ns]格式就是pandas的Timestamp对象

                   ymd  bWendu  yWendu tianqi  ... aqi aqiInfo  aqiLevel    month
ymd                                            ...                               
2018-01-01  2018-01-01       3      -6   晴~多云  ...  59       良         2  2018-01
2018-01-02  2018-01-02       2      -5   阴~多云  ...  49       优         1  2018-01
2018-01-03  2018-01-03       2      -5     多云  ...  28       优         1  2018-01
2018-01-04  2018-01-04       0      -8      阴  ...  28       优         1  2018-01
2018-01-05  2018-01-05       3      -6   多云~晴  ...  50       优         1  2018-01

[5 rows x 10 columns]
DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
               '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08',
               '2018-01-09', '2018-01-10',
               ...
               '2018-12-22', '2018-12-23', '2018-12-24', '2018-12-25',
               '2018-12-26', '2018-12-27', '2018-12-28', '2018-12-29',
               '2018-12-30', '2018-12-31'],
              dtype='datetime64[ns]', name='ymd', length=365, freq=None)

2.19.2 方便的对DatetimeIndex进行查询
#筛选固定的某一天
print(df.loc['2018-01-05'])
#日期区间
print(df.loc['2018-01-05':'2018-01-10'])
#按月份筛选
print(df.loc['2018-03'].head())
#按月份前缀筛选
print(df.loc['2018-07':'2018-09'].head())
#按年份前缀筛选
print(df.loc['2018'].head())
ymd          2018-01-05
bWendu                3
yWendu               -6
tianqi             多云~晴
fengxiang           西北风
fengli             1-2级
aqi                  50
aqiInfo               优
aqiLevel              1
month           2018-01
Name: 2018-01-05 00:00:00, dtype: object
                   ymd  bWendu  yWendu tianqi  ... aqi aqiInfo  aqiLevel    month
ymd                                            ...                               
2018-01-05  2018-01-05       3      -6   多云~晴  ...  50       优         1  2018-01
2018-01-06  2018-01-06       2      -5   多云~阴  ...  32       优         1  2018-01
2018-01-07  2018-01-07       2      -4   阴~多云  ...  59       良         2  2018-01
2018-01-08  2018-01-08       2      -6      晴  ...  50       优         1  2018-01
2018-01-09  2018-01-09       1      -8      晴  ...  34       优         1  2018-01
2018-01-10  2018-01-10      -2     -10      晴  ...  26       优         1  2018-01

[6 rows x 10 columns]
                   ymd  bWendu  yWendu tianqi  ...  aqi aqiInfo  aqiLevel    month
ymd                                            ...                                
2018-03-01  2018-03-01       8      -3     多云  ...   46       优         1  2018-03
2018-03-02  2018-03-02       9      -1   晴~多云  ...   95       良         2  2018-03
2018-03-03  2018-03-03      13       3   多云~阴  ...  214    重度污染         5  2018-03
2018-03-04  2018-03-04       7      -2   阴~多云  ...  144    轻度污染         3  2018-03
2018-03-05  2018-03-05       8      -3      晴  ...   94       良         2  2018-03

[5 rows x 10 columns]
                   ymd  bWendu  yWendu  tianqi  ...  aqi aqiInfo  aqiLevel    month
ymd                                             ...                                
2018-07-01  2018-07-01      35      23       晴  ...   68       良         2  2018-07
2018-07-02  2018-07-02      32      23  多云~雷阵雨  ...   66       良         2  2018-07
2018-07-03  2018-07-03      32      24  雷阵雨~多云  ...   76       良         2  2018-07
2018-07-04  2018-07-04      35      26      多云  ...   96       良         2  2018-07
2018-07-05  2018-07-05      37      24  多云~雷阵雨  ...  104    轻度污染         3  2018-07

[5 rows x 10 columns]
                   ymd  bWendu  yWendu tianqi  ... aqi aqiInfo  aqiLevel    month
ymd                                            ...                               
2018-01-01  2018-01-01       3      -6   晴~多云  ...  59       良         2  2018-01
2018-01-02  2018-01-02       2      -5   阴~多云  ...  49       优         1  2018-01
2018-01-03  2018-01-03       2      -5     多云  ...  28       优         1  2018-01
2018-01-04  2018-01-04       0      -8      阴  ...  28       优         1  2018-01
2018-01-05  2018-01-05       3      -6   多云~晴  ...  50       优         1  2018-01

[5 rows x 10 columns]

2.19.3 方便的获取周、月、季度
#周数字列表
print(df.index.week)
#月数字列表
print(df.index.month)
#季度数字列表
print(df.index.quarter)
Int64Index([ 1,  1,  1,  1,  1,  1,  1,  2,  2,  2,
            ...
            51, 51, 52, 52, 52, 52, 52, 52, 52,  1],
           dtype='int64', name='ymd', length=365)
Int64Index([ 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
            ...
            12, 12, 12, 12, 12, 12, 12, 12, 12, 12],
           dtype='int64', name='ymd', length=365)
Int64Index([1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
            ...
            4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
           dtype='int64', name='ymd', length=365)
2.19.4 统计每周、每月、每个季度的最高温度
#统计每周最高温度
print(df.groupby(df.index.week)["bWendu"].max().head())
#jupyter中画图表
df.groupby(df.index.week)["bWendu"].max().plot()
#统计每月最高温度
print(df.groupby(df.index.month)["bWendu"].max())
#统计每季度最高温度
print(df.groupby(df.index.quarter)["bWendu"].max())
ymd
1    3
2    6
3    7
4   -1
5    4
Name: bWendu, dtype: int32
ymd
1      7
2     12
3     27
4     30
5     35
6     38
7     37
8     36
9     31
10    25
11    18
12    10
Name: bWendu, dtype: int32
ymd
1    27
2    38
3    37
4    25
Name: bWendu, dtype: int32

2.20 pandas怎么处理日期索引的缺失

问题:按日期统计的数据,缺失了某天,导致数据不全该怎么补充日期

  1. DataFrame.reindex,调整dataframe的索引以适应新的索引
  2. Dataframe.resample,可以对时间序列重采样,支持补充缺失值
import pandas as pd 
%matplotlib inline

df = pd.DataFrame({
    "pdate":["2019-12-01","2019-12-02","2019-12-04","2019-12-05"],
    "pv":[100,200,400,500],
    "uv":[10,20,40,50]
})
df.set_index("pdate").plot()

缺失了12-03的数据,如何补充

2.20.1 使用pandas.reindex方法
#将索引变成日期格式
df_date = df.set_index("pdate")
df_date = df_date.set_index(pd.to_datetime(df_date.index))
print(df_date)
#连续的日期
pdates = pd.date_range(start="2019-12-01",end="2019-12-05")
print(pdates)
df_date_new = df_date.reindex(pdates,fill_value=0)
print(df_date_new)
df_date_new.plot()
	pv	uv
pdate		
2019-12-01	100	10
2019-12-02	200	20
2019-12-04	400	40
2019-12-05	500	50

DatetimeIndex(['2019-12-01', '2019-12-02', '2019-12-03', '2019-12-04',
               '2019-12-05'],
              dtype='datetime64[ns]', freq='D')
             pv  uv
2019-12-01  100  10
2019-12-02  200  20
2019-12-03    0   0
2019-12-04  400  40
2019-12-05  500  50
2.20.2 使用pandas.resample方法(重采样)

resample的含义:改变数据的时间频率,比如把天数据变成月份,或者把小时数据变成分钟级别

resample语法:(DataFrame or Series).resample(arguments).(aggregate function)

#设置日期格式索引
df_new2 = df.set_index(pd.to_datetime(df["pdate"])).drop("pdate",axis=1)
print(df_new2)

#按天重采样,因为采样会让区间变成一个值,所以用mean平均值,每天只有一个数据所以平均值就是原值
df_new2 = df_new2.resample("D").mean().fillna(0)
print(df_new2)

#每两天的平均值
print(df_new2.resample("2D").mean())
             pv  uv
pdate              
2019-12-01  100  10
2019-12-02  200  20
2019-12-04  400  40
2019-12-05  500  50
               pv    uv
pdate                  
2019-12-01  100.0  10.0
2019-12-02  200.0  20.0
2019-12-03    0.0   0.0
2019-12-04  400.0  40.0
2019-12-05  500.0  50.0
               pv    uv
pdate                  
2019-12-01  150.0  15.0
2019-12-03  200.0  20.0
2019-12-05  500.0  50.0

2.21 使用pandas category类型压缩数据

  1. 字符转成数字(不影响使用)降低存储空间
  2. 提升运算速度
object列转category类型
columns = ["col1", "col2"]
for col in columns:
    df[col] = df[col].astype("category")

2.22 Pandas的get_dummies用于机器学习的特征处理

one-hot-encoding

2.22.1 读取数据

index_col把第几列当初索引列
read_csv(“path”, index_col=0)

import pandas as pd
df_titanic = pd.read_csv("./titanic/titanic_train.csv")
print(df_titanic.head())
df_titanic.drop(columns=["Name","Ticket","Cabin"],inplace=True)
print(df_titanic.head())
print(df_titanic.info())
   PassengerId  Survived  Pclass  ...     Fare Cabin  Embarked
0            1         0       3  ...   7.2500   NaN         S
1            2         1       1  ...  71.2833   C85         C
2            3         1       3  ...   7.9250   NaN         S
3            4         1       1  ...  53.1000  C123         S
4            5         0       3  ...   8.0500   NaN         S

[5 rows x 12 columns]
   PassengerId  Survived  Pclass     Sex   Age  SibSp  Parch     Fare Embarked
0            1         0       3    male  22.0      1      0   7.2500        S
1            2         1       1  female  38.0      1      0  71.2833        C
2            3         1       3  female  26.0      0      0   7.9250        S
3            4         1       1  female  35.0      1      0  53.1000        S
4            5         0       3    male  35.0      0      0   8.0500        S
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 9 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Sex          891 non-null    object 
 4   Age          714 non-null    float64
 5   SibSp        891 non-null    int64  
 6   Parch        891 non-null    int64  
 7   Fare         891 non-null    float64
 8   Embarked     889 non-null    object 
dtypes: float64(2), int64(5), object(2)
memory usage: 62.8+ KB
None
2.22.2 填充age的空值
df_titanic["Age"] = df_titanic["Age"].fillna(df_titanic["Age"].mean())
print(df_titanic.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 9 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Sex          891 non-null    object 
 4   Age          891 non-null    float64
 5   SibSp        891 non-null    int64  
 6   Parch        891 non-null    int64  
 7   Fare         891 non-null    float64
 8   Embarked     889 non-null    object 
dtypes: float64(2), int64(5), object(2)
memory usage: 62.8+ KB
None
2.22.3 普通无序分类用get_dummies编码
print(pd.get_dummies(df_titanic["Sex"]).head())
   female  male
0       0     1
1       1     0
2       1     0
3       1     0
4       0     1

df转换

#df多列转换
needcode_cat_columns = ["Pclass","Sex","SibSp","Parch","Embarked"]
df_coded = pd.get_dummies(
    df_titanic,
    #要转码的列
    columns=needcode_cat_columns,
    #生成的列名的前缀
    prefix=needcode_cat_columns,
    #把空值也做编码
    dummy_na=True,
    #把1 of k移除(dummy variable trap)
    drop_first=True
)
print(df_coded.head())
   PassengerId  Survived   Age  ...  Embarked_Q  Embarked_S  Embarked_nan
0            1         0  22.0  ...           0           1             0
1            2         1  38.0  ...           0           0             0
2            3         1  26.0  ...           0           1             0
3            4         1  35.0  ...           0           1             0
4            5         0  35.0  ...           0           1             0

[5 rows x 26 columns]

2.22.4 机器学习模型训练
y = df_coded.pop("Survived")
print(y.head())
X = df_coded
#X中没有Survived这一列了
print(X.head())
from sklearn.linear_model import LogisticRegression
#创建模型对象
logreg = LogisticRegression(solver='liblinear')
#实现模型训练
logreg.fit(X,y)
#准确率
print(logreg.score(X,y))
0    0
1    1
2    1
3    1
4    0
Name: Survived, dtype: int64
   PassengerId   Age     Fare  ...  Embarked_Q  Embarked_S  Embarked_nan
0            1  22.0   7.2500  ...           0           1             0
1            2  38.0  71.2833  ...           0           0             0
2            3  26.0   7.9250  ...           0           1             0
3            4  35.0  53.1000  ...           0           1             0
4            5  35.0   8.0500  ...           0           1             0

[5 rows x 25 columns]
0.813692480359147

2.23 Pandas实现groupby聚合后不同列数据统计

单指标统计:每个MovieID平均分

df.groupby("MovieID")["Rating"].mean()

单列多指标统计:每个MovieID的最高、最低、平均分

# =前是新df的列名,后面是函数名或者np中的函数
# agg(新列名=函数)
df.groupby("MovieID")["Rating"].agg(mean="mean",max="max",min=np.min)

df.groupby("MovieID").agg({"Rating":['mean','max',np.min]})

多个列多个指标统计:每个MovieID的评分人数,最高评分、最低、平均分

  1. 方法一 agg(新列名=(原列名,函数))
df_new = df.groupby("MovieID").agg(rating_mean=("Rating","mean"),user_count=("UserID",lambda x:x.nunique()))

out

         rating_mean  user_count
MovieID                         
1           4.146846        2077
2           3.201141         701
3           3.016736         478
4           2.729412         170
5           3.006757         296
...              ...         ...
  1. 方法二 agg({“原列名”,函数/列表})
    agg函数等号代表把结果赋值给新列,字段/元组代表 对整个列运用这些函数
df_new = df.groupby("MovieID").agg({"Rating":['mean','min','max'],"UserID":lambda x:x.nunique()})
print(df_new)
           Rating           UserID
            mean min max <lambda>
MovieID                           
1        4.146846   1   5     2077
2        3.201141   1   5      701
3        3.016736   1   5      478
4        2.729412   1   5      170
5        3.006757   1   5      296
...           ...  ..  ..      ...

将二级索引变成一级索引

#将2级索引变成1级索引
df_new.columns = ['ra_mean','ra_min','ra_max','user_cou']
print(df_new)
          ra_mean  ra_min  ra_max  user_cou
MovieID                                    
1        4.146846       1       5      2077
2        3.201141       1       5       701
3        3.016736       1       5       478
4        2.729412       1       5       170
5        3.006757       1       5       296
...           ...     ...     ...       ...
  1. 方法三 apply
df_new = df.groupby("MovieID").apply(lambda x:pd.Series({"min":x["Rating"].min(),"mean":x["Rating"].mean()}))
print(df_new)
         min      mean
MovieID               
1        1.0  4.146846
2        1.0  3.201141
3        1.0  3.016736
4        1.0  2.729412
5        1.0  3.006757
...      ...       ...

2.24 Pandas实现Excel的vlookup函数

2.24.1 关联
import pandas as pd

df_grade = pd.read_excel("./c23_excel_vlookup/学生成绩表.xlsx")
df_sinfo = pd.read_excel("./c23_excel_vlookup/学生信息表.xlsx")
print(df_grade.head())
#两个表关联
#筛选几行
df_sinfo = df_sinfo[["学号","姓名","性别"]]
print(df_sinfo.head())
df_merge = pd.merge(left=df_grade,right=df_sinfo,left_on="学号",right_on="学号")
print(df_merge.head())
    班级    学号  语文成绩  数学成绩  英语成绩
0  C01  S001    99    84    88
1  C01  S002    66    95    77
2  C01  S003    68    68    61
3  C01  S004    63    66    82
4  C01  S005    72    95    94
     学号  姓名 性别
0  S001  怠涵  女
1  S002  婉清  女
2  S003  溪榕  女
3  S004  漠涓  女
4  S005  祈博  女
    班级    学号  语文成绩  数学成绩  英语成绩  姓名 性别
0  C01  S001    99    84    88  怠涵  女
1  C01  S002    66    95    77  婉清  女
2  C01  S003    68    68    61  溪榕  女
3  C01  S004    63    66    82  漠涓  女
4  C01  S005    72    95    94  祈博  女
2.24.2 调整列顺序(python语法)

将姓名、性别调整到学号后

#转成python list
new_columns = df_merge.columns.to_list()
#[::-1]逆向先取性别再取姓名,insert的时候会再颠倒一次保证姓名、性别顺序
for name in ["姓名","性别"][::-1]:
    new_columns.remove(name)
    new_columns.insert(new_columns.index("学号")+1, name)
print(new_columns)
#使用new_columns重新索引
df_merge = df_merge.reindex(columns=new_columns)
print(df_merge.head())
#输出Excel
df_merge.to_excel("./c23_excel_vlookup/合并后的数据表.xlsx", index=False)
['班级', '学号', '姓名', '性别', '语文成绩', '数学成绩', '英语成绩']
    班级    学号  姓名 性别  语文成绩  数学成绩  英语成绩
0  C01  S001  怠涵  女    99    84    88
1  C01  S002  婉清  女    66    95    77
2  C01  S003  溪榕  女    68    68    61
3  C01  S004  漠涓  女    63    66    82
4  C01  S005  祈博  女    72    95    94
2.25 pandas集合pyecharts绘制交互性折线图(jupyter中)

echarts是百度开源的可视化图表库,pyechart是python库版本

import pandas as pd
df = pd.read_excel(r"C:\Users\Administrator\Desktop\jupyter_data\ant-learn-pandas\datas\stocks\baidu_stocks.xlsx", index_col="datetime", parse_dates=True)
df.sort_index(inplace=True)
df.head()

out

code	open	close	high	low	vol	p_change
datetime							
2019-01-02	BIDU	156.179993	162.250000	164.330002	155.490005	2996952	NaN
2019-01-03	BIDU	158.750000	154.710007	159.880005	153.779999	3879180	-4.65
2019-01-04	BIDU	157.600006	160.949997	162.429993	157.250000	3847497	4.03
2019-01-07	BIDU	162.600006	162.600006	164.490005	158.509995	3266091	1.03
2019-01-08	BIDU	162.190002	163.399994	163.889999	158.160004	3253361	0.49
#使用pyecharts绘制折线图 pip install pyecharts
from pyecharts.charts import Line
from pyecharts import options as opts
#折线图
line = Line()
#x轴
line.add_xaxis(df.index.to_list())
#每个y轴
line.add_yaxis("开盘价", df["open"].round(2).to_list())
line.add_yaxis("收盘价", df["close"].round(2).to_list())
#图表配置
#TitleOpts设置标题
#TooltipOpts设置动态展示十字标
line.set_global_opts(
    title_opts = opts.TitleOpts(title="百度股票2019"),
    tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross")
)
#渲染数据,画图
line.render_notebook()

out
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wF5o9bso-1680050072813)(http://image.suifengxing.cn/note/python/pyecharts%E6%8A%98%E7%BA%BF%E5%9B%BE.PNG)]

2.26 pandas explode方法将一行变多行

pandas.Dataframe.explode(column),将dataframe的一个list-like的元素按行复制,index索引随之复制

import pandas as pd
df = pd.read_csv(r"C:\Users\Administrator\Desktop\jupyter_data\ant-learn-pandas\datas\movielens-1m\movies.dat",
                header=None,
                 names="MovieID::Title::Generes".split("::"),
                 sep="::",
                 engine="python"
                )
# print(df.head())
#将Genenres字段拆出list类型字段
df["Genere"] = df["Generes"].map(lambda x:x.split("|"))
# print(df.head())
df_new = df.explode("Genere")
print(df_new.head(10))
%matplotlib inline
df_new["Genere"].value_counts().plot.bar()
   MovieID                     Title                       Generes      Genere
0        1          Toy Story (1995)   Animation|Children's|Comedy   Animation
0        1          Toy Story (1995)   Animation|Children's|Comedy  Children's
0        1          Toy Story (1995)   Animation|Children's|Comedy      Comedy
1        2            Jumanji (1995)  Adventure|Children's|Fantasy   Adventure
1        2            Jumanji (1995)  Adventure|Children's|Fantasy  Children's
1        2            Jumanji (1995)  Adventure|Children's|Fantasy     Fantasy
2        3   Grumpier Old Men (1995)                Comedy|Romance      Comedy
2        3   Grumpier Old Men (1995)                Comedy|Romance     Romance
3        4  Waiting to Exhale (1995)                  Comedy|Drama      Comedy
3        4  Waiting to Exhale (1995)                  Comedy|Drama       Drama
2.27 爬取一个网页
2.27.1 通过Cookies插件设置cookie

Chrome插件可以复制cookie为json格式:https://www.editthiscookie.com/
JSON格式cookies复制到cookie.txt中

import requests.cookies
cookie_jar = requests.cookies.RequestsCookieJar()
with open("./cookie.txt") as fin:
    cookieJson = json.loads(fin.read())
    for cookie in cookieJson:
        cookie_jar.set(
            name=cookie["name"],
            value=cookiep["value"],
            domain=cookie["domain"],
            path=cookie["path"]
        )
    
2.27.2 下载html
htmls = []
url = "http://dict.youdao.com/wordbook/wordlist?p={idx}&tags="
for idx in range(6):
    time.sleep(1)
    print("爬取数据第%d页" % idx)
    r = requests.get(url.format(idx=idx), cookies=cookie_jar)
    htmls.append(r.text)
2.27.3 pandas读取处理、拼接
# 拼接多个html df
df_list = []
for html in htmls:
    df = pd.read_html(html)
    df_cont = df[1]
    # 这里的df[0]是标头
    df_cont.columns = df[0].columns
    df_list.append(df_cont)
# 拼接多个html解析出的df
df_all = pd.concat(df_list)
2.28 将多个列合并然后拆分explode
import pandas as pd

file_path = r"./c39_explode_to_manyrows/读者提供的数据-输入.xlsx"
df = pd.read_excel(file_path)
print(df.head())
# 拿Supplier之后所有列名
merge_name = list(df.loc[:, "Supplier":].columns.values)
print(merge_name)
#把Supplier和Supplier PN合并成一个大字符串
def merge_column(x):
    """
    x是一个series,把他们按分隔符合并
    :param x:
    :return:
    """
    #删除为空的列
    x = x[x.notna()]
    # 使用x.values用于合并
    y = x.values
    # 合并后的列表,每个元素是Supplier Supplier PN 对
    result = []
    # range的步长为2,目的是每两列做合并
    for idx in range(0, len(y), 2):
        # 竖线是分隔符
        result.append(f"{y[idx] }|{y[idx + 1]}")
    # #分隔每个键值对
    return "#".join(result)

#axis=1表示将每行作为series传入merge_column方法中
df["merge"] = df.loc[:,"Supplier":].apply(merge_column, axis=1)
print(df)
# 删除不需要的列 axis=1按列删除
df.drop(merge_name, axis=1, inplace=True)
print(df)
print(df.columns)

# 使用explode变成多行,先将merge列变为list形式才行
df["merge"] = df["merge"].str.split("#")
print(df)
df_explode = df.explode("merge")
print(df_explode)

# 将Supplier和Supplier PN对拆成两列
df_explode["Supplier"] = df_explode["merge"].str.split("|").str[0]
df_explode["Supplier PN"] = df_explode["merge"].str.split("|").str[1]

print(df_explode.columns)
print(df_explode)
df_explode.to_excel("./c39_explode_to_manyrows/读者提供的数据-输出.xlsx", index=False)
2.29 将多个列合并
import pandas as pd

df = pd.read_csv("./tianqi/beijing_tianqi_2018.csv")

df["bWendu"] = df["bWendu"].str.replace("℃","").astype("int32")
df["yWendu"] = df["yWendu"].str.replace("℃","").astype("int32")
print(df.head())

df["ymd"] = pd.to_datetime(df["ymd"])
print(df.info())

# 可以用Series.unique去重
# 可以用",".join(series)实现数组合并成大字符串
print(",".join(df["fengxiang"].unique()))
result = (
    df.groupby(df["ymd"].dt.month).agg(
        # 新列名 = (原列名, 函数)
        最高温度=("bWendu", "max"),
        最低温度=("bWendu", "min"),
        风向列表=("fengxiang", lambda x : ",".join(x.unique())),
        空气质量列表=("aqiInfo", lambda x : ",".join(x.unique()))
    )
    .reset_index() # 把年月日索引变成了普通的列
    .rename(columns={"ymd":"月份"}))
print(result)
    月份  最高温度  最低温度                         风向列表                   空气质量列表
0    1     7    -4     东北风,北风,西北风,西南风,南风,东南风,东风            良,优,轻度污染,中度污染
1    2    12    -1      北风,西南风,南风,西北风,西风,东北风,东风       良,优,轻度污染,中度污染,重度污染
2    3    27     4         西南风,北风,东南风,南风,东北风,东风  优,良,重度污染,轻度污染,中度污染,严重污染
3    4    30    10        南风,北风,东北风,西南风,西北风,东南风       重度污染,良,优,轻度污染,中度污染
4    5    35    18  东北风,北风,西南风,南风,东南风,东风,西风,西北风            轻度污染,优,良,中度污染
5    6    38    23         西南风,南风,北风,东风,东南风,东北风            良,轻度污染,优,中度污染
6    7    37    25      东南风,西南风,南风,东北风,东风,西风,北风                 良,轻度污染,优
7    8    36    26         东南风,南风,东风,东北风,北风,西南风                 良,轻度污染,优
8    9    31    19                南风,北风,西南风,西北风                 优,良,轻度污染
9   10    25    14         北风,西北风,南风,西风,东北风,西南风            优,良,轻度污染,中度污染
10  11    18     7        南风,北风,西南风,东南风,西北风,东北风       良,轻度污染,重度污染,优,中度污染
11  12    10    -5              东南风,东北风,西北风,西南风       中度污染,重度污染,良,优,轻度污染

2.30 将Excel一列拆分多列
import pandas as pd

df = pd.read_excel("./c42_split_onecolumn_tomany/学生数据表.xlsx")
print(df.head())
print(df.info())


def split_column(line):
    line["姓名"], line["性别"], line["年龄"], line["籍贯"]  = line["数据"].split(":")
    return line
df = df.apply(split_column, axis=1)
print(df.head())
df = df.drop("数据", axis=1)
print(df.head())
2.31 按行遍历dataframe
for row in df.itertuples():
    print(row)
    print(row.Index, row.学号, row.姓名)
2.32 合并多个Excel

每个Excel和sheet格式相同

import pandas as pd
import os

dfs = []

# sheet_name=None读所有sheet
for fname in os.listdir("./concat/"):
    if fname.endswith(".xls") and fname != "final.xls":
        df = pd.read_excel(
            fname,
            header=None,
            sheet_name=None
        )
        # df是 key=sheetName:value=df的字典。list.extend方法添加多项
        dfs.extend(df.values())
result = pd.concat(dfs)
result.to_excel("./concat/final.xls", index=False)
2.33 横竖转换transpose
import pandas as pd

df = pd.read_excel("./c23_excel_vlookup/学生信息表.xlsx")
print(df.head())
df2 = df.transpose()
print(df2.head())
     学号  姓名 性别  年龄  籍贯
0  S001  怠涵  女  23  山东
1  S002  婉清  女  25  河南
2  S003  溪榕  女  23  湖北
3  S004  漠涓  女  19  陕西
4  S005  祈博  女  24  山东
      0     1     2     3     4     5   ...    18    19    20    21    22    23
学号  S001  S002  S003  S004  S005  S006  ...  S019  S020  S021  S022  S023  S024
姓名    怠涵    婉清    溪榕    漠涓    祈博    孝冉  ...    冬梅    箐莲    仪博    馨苒    云霖    海彤
性别     女     女     女     女     女     女  ...     女     女     女     男     男     男
年龄    23    25    23    19    24    22  ...    23    24    21    22    23    23
籍贯    山东    河南    湖北    陕西    山东    河南  ...    湖北    陕西    山东    河南    湖北    陕西

[5 rows x 24 columns]
2.33.2 列重排列
columns = list(df.columns)
columns.remove("年龄")
columns.insert(columns.index("姓名"), "年龄")
print(columns)
df3 = df[columns]
print(df3.head())
['学号', '年龄', '姓名', '性别', '籍贯']
     学号  年龄  姓名 性别  籍贯
0  S001  23  怠涵  女  山东
1  S002  25  婉清  女  河南
2  S003  23  溪榕  女  湖北
3  S004  19  漠涓  女  陕西
4  S005  24  祈博  女  山东
2.34 pandas查询数据简单方法df.query

大数据量时,传给c语言实现,更快

Dataframe.query(expr,inplace=False,**kwargs)

expr为要返回boolean结果的字符串表达式,如:df.query('a<100') df.query('a < b & b < c')

语法:

img

import pandas as pd

df = pd.read_csv(r'tianqi/beijing_tianqi_2018.csv')
print(df.index)
df.set_index('ymd',inplace=True)

df.loc[:,'bWendu'] = df['bWendu'].str.replace('℃','').astype('int32')
df.loc[:,'yWendu'] = df['yWendu'].str.replace('℃','').astype('int32')

# 使用普通条件查询,必须有括号,不然有优先级问题
print(df[df["yWendu"] < -10].head())
df1 = df[(df["bWendu"]<=30)&(df["yWendu"]>=15)&(df["tianqi"]=="晴")]
print(df1.head())

#使用df.query简化查询
# Dataframe.query(expr,inplace=False,**kwargs)

print(df.query("yWendu < 3").head(3))
# 查温差大于15度的数据
print(df.query("bWendu - yWendu >= 15").head(3))
# 可使用外部变量 @符号
high_temp = 15
low_temp = 13
print(df.query("yWendu<=@high_temp & yWendu>=@low_temp").head(3))
2.34.1 df.query出来的df想和原df脱离关系,直接用copy
df_new = df.query("yWenDu == 20").copy()

3、练习部分

3.1 创建series和dataframe

import pandas as pd

# list dict to series

list = ["数学","语文","英语"]
s = pd.Series(data=list)
# 索引是自动填充的数字
print(s)

dict = {"数学": 80, "语文": 90, "英语": 120}
s1 = pd.Series(data=dict)
# 索引是dict的key
print(s1)

# series to list/dict

numbers = s1.to_list()
print(numbers)
d = s1.to_dict()
print(d)

# series to dataframe

df = pd.DataFrame(data=s1, columns=["grade"])
print(df)

import numpy as np

# 借助numpy创建series
s = pd.Series(
    np.arange(10, 100, 10),# 10-90,间隔10
    index=np.arange(101, 110),# 索引:101-109,间隔1
    dtype='float'# 类型float64
)
print(s)

# 字典创建dataframe
df = pd.DataFrame(
    data={
        "姓名": ["小张","小李"],
        "年龄":[18,19]
    }
)
print(df)
# 设置索引列
df.set_index("姓名", inplace=True)
print(df.index)

3.2 列类型转换

import pandas as pd

s = pd.Series(
    data=["001","002","003","004"],
    index=list("abcd")
)
# 数值转换成数字类型
s = s.apply(lambda x: int(x))
print(s)

# 或者astype
s = s.astype(int)
print(s)

# 存在的series数据添加元素
s = s.append(pd.Series(
    data=[6,7],
    index=['f','g']
))

print(s)

# series转换成dataframe
df = s.reset_index()
df.columns=["idx","cnt"]
print(df)

3.3 时间数据处理

import pandas as pd
# 生成一个月的所有日期
date_range = pd.date_range(start='2021-10-01', end='2022-10-31')
# periods按天递增取31个数
date_range = pd.date_range(start='2021-10-01', periods=31)
print(date_range)

# 输出2021年10月所有周一的日期列表。freq是频率,W-MON表示周的第一天,freq可以参考官方文档
date_range = pd.date_range(start='2021-10-01', periods=31, freq='W-MON')
print(date_range)

# 生成2021-10-01当天所有小时时间
date_range = pd.date_range(start='2021-10-01', periods=24, freq='H')
print(date_range)

# 输出dataframe,包含2021-10月的31天,以及每天是在2021年的第几天
date_range = pd.date_range(start='2021-10-01', end='2022-10-31')
df = pd.DataFrame(data=date_range, columns=['day'])
# df['day']是日期格式,直接取.dt,dt上有日期操作属性。dayofyear这个日期在本年度的第几天
df['day_of_year'] = df['day'].dt.dayofyear
print(df)

# 生成日期和随机分布的dataframe
import numpy as np
date_range = pd.date_range(start='2021-01-01', periods=1000)
data = {
    'norm' : np.random.normal(loc=0, scale=1, size=1000),# 正态分布1000个随机数
    'uniform': np.random.uniform(low=0, high=1, size=1000),# 均匀分布1000个随机数
    'binomial': np.random.binomial(n=1, p=0.2, size=1000)# 二项分布1000个随机数
}
df = pd.DataFrame(data=data, index=date_range)
print(df)

# 统计数据列的值出现次数 pd.Series.value_counts()
print(df['binomial'].value_counts())

3.4 股票数据处理

import pandas as pd

df = pd.read_csv("./00700.HK.csv", index_col=0)
# 找出收盘价最低的一行
print(df["Close"].min())

print(df["Close"].min())
# argmin返回最小值所在索引;df.loc[[]]两个中括号返回个dataframe,一个[]返回series
print(df.loc[[df["Close"].argmin()]])

# 列重命名
# columns直接等于新列表覆盖原名称
df.columns = ["A","B"]
# rename,字典的方式,key原列,value新列名
df.rename(columns={"Date":"D","open":"o"},inplace=True)

3.5 数字和df列处理

import pandas as pd
import numpy as np

np.random.seed(66)
s1 = pd.Series(np.random.rand(20))
s2 = pd.Series(np.random.randn(20))

df = pd.concat([s1,s2], axis=1)
df.columns = ['col1', 'col2']
print(df)

# 截断现有数列,小于-1的为-1,大于1的为1
df['col3'] = df['col2'].clip(-1.0, 1.0)
print(df)

# 取最大的五个数
print(df['col1'].nlargest(5))
print(df['col1'].nsmallest(5))

# 累计加和值-每一行的值等于前面所有行的和
print(df.cumsum())

# 计算中位数-某一列从小到大排列取中间的数字 = 50%分位数
print(df['col2'].median())
print(df['col2'].quantile())

# dataframe变成字典
print(df.head(5).to_dict())
import pandas as pd
import numpy as np

np.random.seed(66)
# 随机10行4列dataframe
df = pd.DataFrame(np.random.rand(10, 4), columns=list('ABCD'))

# 使用loc筛选,类似df.query
print(df.loc[df['C'] > 0.8])
print(df.loc[(df['C'] > 0.3) & (df['D'] < 0.7)])

# for循环遍历dataframe。index索引,row数据
for index, row in df.head().iterrows():
    print(row)

# 指定特定单元格的值:iloc设置数字索引值、loc设计行标签的值
# iloc 第一个参数是数字索引值;第二个参数是列数,1是第2行 B
df.iloc[3, 1] = np.nan
# loc 第一个参数是行标签,第9行;第二个参数是列名
df.loc[8, 'D'] = np.nan
print(df)

# 移除包含空值得行
df2 = df.dropna()
print(df2)

# 重置索引,让索引连续
df2 = df2.reset_index(drop=True)
print(df2)

# 统计每列空值个数。isnull返回true和false,sum时true=1 false=0
print(df.isnull().sum())

# 填充空值
df = df.fillna(0)
print(df)

# 修改列顺序
df = df[['D','A','B','C']]
print(df)

# 删除列
print(df.drop("D", axis=1))
print(df.drop(["C","D"], axis=1))

3.6 np.where用法 (三元表达式?)

import pandas as pd
import numpy as np

df = pd.read_csv("")
# np.where用法值为null bhp时替换成真的空值,否则不变
df["power"] = np.where(df["power"] == "null bhp", np.nan, df["power"])

3.6 重复行处理

df.duplicated()筛选出重复行

# 和前面重复了返回true,否则false的series
print(df.duplicated())
# 找出那个行和前面重复
print(df[df.duplicated()])
# 删除重复行
df = df.drop_duplicates()

3.6.1 筛选指定类型的列

# 筛选类型为object的列
print(df.select_dtypes(include=["object"]))

3.6.2 描述信息矩阵转置

# 行变列,列变行
print(df.describe().T)

3.6.3 category类型字段,有函数直接将维度字符串转为数字0/1

# drop_first,比如转成了男女为两个字段,转为0 1,会去掉第一行,只用一个男=0/1标识即可
# get_dummies会将一列转为多列,数量为里面字符串去重后的个数
df_dummies = pd.get_dummies(df, drop_first=True)
print(df.info())

3.6.4 查看相关矩阵corr

字符串数据列不会出现

# 返回的也是个dataframe
corr = df_dummies.corr()
筛选
print(corr[["charges"]].sort_values(by="charges",ascending=False))
df中某列pop
data = df_dummies.copy()
target = data.pop("charges")
# target是charges这一列的series

3.7 pandas加载json文件

pd.read_json();

json格式是dict like:

# 0 1 是索引数值
{
    "col1":{
        "0":"11",
        "1":"22"
    },
    "col2":{
        "0":"33",
        "1":"44"
    }
}

3.7.1 筛选某一列中某个值数量大于1的列

# 每个值出现的次数
df_counts = df["country"].value_counts()
# 出现次数大于1的,返回true和false的series
print(df_counts > 1)
# 只输出数量大于1的值
print(df_counts[df_counts > 1])

3.7.2 找最大值所在的列

  1. argmax返回最大值所在的索引。
  2. argmin返回最小值所在的索引。
print(df.iloc[df['value'].argmax())

3.8按索引(日期)拼接两个dataframe

# 索引相同的列放到了同一行中
pd.concat([df1, df2], axis=1)

3.8.1 两个列的值计算出新的列

# 计算股票的变动百分比 (收盘/开盘 - 1)*100
df['bidu_change'] = (df['bidu_close'] / df['bidu_open' - 1) * 100

3.8.2 df.copy(),子dataframe新增行列时报错,需要copy为新的df,避免和原df冲突

3.8.3 判断两个列是否同为正负(股票同涨同跌)

# 判断>0是布尔类型series数据,再*1变为数字 true->1 false->0
df['flag'] = (df['col1']*df['col2'] > 0) * 1

3.8.4 dataframe的行数

len(df)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值