前语

开刷Pandas数据剖析,看起来很好理解,不过没做笔记没敲代码心里总是不安稳,所以复现下课程代码并演示其间遇到的问题,顺便水一水笔记好了

参考资料:

课程视频链接:Pandas数据剖析从入门到实战

数据及代码示例:ant-learn-pandas: pandas学习课程代码库房 (gitee.com)

一、数据读取

0. 数据类型

数据类型 阐明 Pandas读取办法
csv, tsv, txt 用逗号、tab或其它字符分割的文本文件 read_csv
excel xls或xlsx文件 read_excel
mysql 联系型数据表 read_sql

1. read_csv

1.1 读取csv文件

csv是以逗号分割的文本文件,如下:

userId,movieId,rating,timestamp
1,1,4.0,964982703
1,3,4.0,964981247
1,6,4.0,964982224
1,47,5.0,964983815

直接运用read_csv读取该文件

fpath = "../datas/ml-latest-small/ratings.csv"
# 读取
ratings = pd.read_csv(fpath)

1.2 指定分割符

已知access_pvuv.txt如下:

2019-09-10  139    92
2019-09-09  185    153
2019-09-08  123    59
2019-09-07  65     40
2019-09-06  157    98
2019-09-05  205    151
2019-09-04  196    167
2019-09-03  216    176
2019-09-02  227    148
2019-09-01  105    61

为read_csv增加参数delimiter (或seq) 指定分隔符,header=None表明没有第一队伍名称

fpath = "../datas/crazyant/access_pvuv.txt"
pvuv = pd.read_csv(
    fpath,
    delimiter='t',
    header=None,
    names=['date', 'pv', 'uv']
)

2. read_excel

fpath = "../datas/crazyant/access_pvuv.xlsx"
pvuv = pd.read_excel(fpath)
print(pvuv)
'''
输出成果
           日期   PV   UV
0  2019-09-10  139   92
1  2019-09-09  185  153
2  2019-09-08  123   59
3  2019-09-07   65   40
4  2019-09-06  157   98
5  2019-09-05  205  151
6  2019-09-04  196  167
7  2019-09-03  216  176
8  2019-09-02  227  148
9  2019-09-01  105   61
'''

3. read_sql

3.1 运用pymysql

连接数据库,挑选编码方式

import pandas as pd
import pymysql
conn = pymysql.connect(
    host='localhost',
    user='root',
    password='password',
    database='dbname',
    charset='utf8'
)
table = pd.read_sql("select * from tbname", con=conn)
print(table)
'''
输出成果
   id                       preorder_traversal_string
0   1             4_2_1_0_#_#_#_3_#_#_8_7_#_#_11_#_#_
1   2  5_4_2_#_#_1_7_#_6_#_#_#_3_0_#_5_1_#_#_#_6_#_#_
2   3                  6_#_7_5_3_#_#_1_#_#_2_8_#_#_#_
'''

可是会报正告:

UserWarning: pandas only supports SQLAlchemy connectable (engine/connection) or database string URI or sqlite3 DBAPI2 connection. Other DBAPI2 objects are not tested. Please consider using SQLAlchemy.

这儿主张我们运用SQLAlchemy

3.2 运用SQLAlchemy

import pandas as pd
from sqlalchemy import create_engine
host = "127.0.0.1"
user = "root"
password = "password"
database = "dbname"
engine = create_engine(f"mysql pymysql://{user}:{password}@{host}/{database}")
sql = 'select * from tbname'
table = pd.read_sql(sql=sql, con=engine)
print(table)
'''
输出成果
   id                       preorder_traversal_string
0   1             4_2_1_0_#_#_#_3_#_#_8_7_#_#_11_#_#_
1   2  5_4_2_#_#_1_7_#_6_#_#_#_3_0_#_5_1_#_#_#_6_#_#_
2   3                  6_#_7_5_3_#_#_1_#_#_2_8_#_#_#_
'''

4. 检查数据格式

# 检查前几行数据,默认为5行
print(ratings.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
'''
# 检查数据的形状,回来(行数、列数)
print(ratings.shape)
'''
输出成果
(100836, 4)
'''
# 检查列名列表
print(ratings.columns)
'''
输出成果
Index(['userId', 'movieId', 'rating', 'timestamp'], dtype='object')
'''
# 检查索引列
print(ratings.index)
'''
输出成果
RangeIndex(start=0, stop=100836, step=1)
'''
# 检查每列的数据类型
print(ratings.dtypes)
'''
输出成果
userId         int64
movieId        int64
rating       float64
timestamp      int64
dtype: object
'''

二、数据结构

1. Series

import pandas as pd
# 创立Series
s1 = pd.Series(list('abcd'))
print(s1)
'''
0    a
1    b
2    c
3    d
dtype: object
'''
# 指定索引创立Series
s2 = pd.Series(list('efgh'), index=list('abcd'))
print(s2)
print(s2.index)
'''
a    e
b    f
c    g
d    h
dtype: object
Index(['a', 'b', 'c', 'd'], dtype='object')
'''
# 字典创立Series
dict = {
    'a': 'e',
    'b': 'f',
    'c': 'g',
    'd': 'h'
}
s3 = pd.Series(dict)
print(s3)
'''
a    e
b    f
c    g
d    h
dtype: object
'''

2. DataFrame

import pandas as pd
# 字典创立DataFrame
data = {
        'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]
    }
df = pd.DataFrame(data)
print(df)
'''
    state  year  pop
0    Ohio  2000  1.5
1    Ohio  2001  1.7
2    Ohio  2002  3.6
3  Nevada  2001  2.4
4  Nevada  2002  2.9
'''
# 输出DataFrame的索引、列标签以及数据类型
print(df.index, 'nn', df.columns, 'nn', df.dtypes)
'''
RangeIndex(start=0, stop=5, step=1)
 Index(['state', 'year', 'pop'], dtype='object')
 state     object
year       int64
pop      float64
dtype: object
'''

三、查询数据

0. 查询办法

  1. df.loc :根据标签索引,成果包括最终一个标签的值
  2. df.iloc:根据位置索引,成果不包括最终一个位置的值
  3. df.where
  4. df.query

本节首要介绍df.loc

1. 数据预处理

import pandas as pd
# 数据预处理
df = pd.read_csv("../datas/beijing_tianqi/beijing_tianqi_2018.csv")
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       优   
'''
# 设定索引为日期,方便按日期挑选
df.set_index('ymd', inplace=True)
# 替换掉温度的后缀℃
df.loc[:, "bWendu"] = df["bWendu"].str.replace("℃", "").astype('int32')
df.loc[:, "yWendu"] = df["yWendu"].str.replace("℃", "").astype('int32')
# df[df.columns["bWendu"]] = df["bWendu"].str.replace("℃", "").astype('int32')
# df[df.columns["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       优    
'''

这儿会报一个正告:

 DeprecationWarning: In a future version, `df.iloc[:, i] = newvals` will attempt to set the
 values inplace instead of always setting a new array. To retain the old behavior, use either
 `df[df.columns[i]] = newvals` or, if columns are non-unique, `df.isetitem(i, newvals)`

2. 按数值、列表、区间查询

# 得到单个值
single_value = df.loc['2018-01-03', 'bWendu']
# 得到一列/一行
s1 = df.loc['2018-01-03', ['bWendu', 'yWendu']]
s2 = df.loc[['2018-01-03', '2018-01-04', '2018-01-05'], 'bWendu']
# 得到DataFrame
df2 = df.loc[['2018-01-03','2018-01-04','2018-01-05'], ['bWendu', 'yWendu']]
# 按区间查询
df3 = df.loc['2018-01-03':'2018-01-05', 'bWendu':'fengxiang']

3. 条件查询

# 查询最高温度小于30度,而且最低温度大于15度,而且是晴天,而且气候为优的数据
df4 = df.loc[(df["bWendu"] <= 30) & (df["yWendu"] >= 15)
        & (df["tianqi"] == '晴') & (df["aqiLevel"] == 1), :]
print(df4)
'''
            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
'''

其间,条件表达式回来的是一个布尔值的Series

# 观察条件表达式
print(df["yWendu"] < -10)
'''
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
'''

4. 函数查询

# 直接写lambda表达式
df5 = df.loc[lambda df : (df["bWendu"] <= 30) & (df["yWendu"] >= 15), :]
# 编写自己的函数,查询9月份,空气质量好的数据
def query_my_data(df):
    return df.index.str.startswith("2018-09") & (df["aqiLevel"] == 1)
df6 = df.loc[query_my_data, :]

四、新增数据列

1. 直接赋值

df.loc[:, 'wencha'] = df['bWendu'] - df['yWendu']

2. df.apply

传入一个函数并选定axis:

  1. 当axis=1,函数的参数为一行的Series(常用)
  2. 当axis=0,函数的参数为一列的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)

3. df.assign

df.assign总是会创立一个新的copy

运用lambda表达式,处理本来的数据得到新列

# 能够同时增加多个新的列
df.assign(
    yWendu_huashi = lambda x : x["yWendu"] * 9 / 5   32,
    # 摄氏度转华氏度
    bWendu_huashi = lambda x : x["bWendu"] * 9 / 5   32
)

4. 条件挑选分组后赋值

# 先创立空列(这是第一种创立新列的办法)
df['wencha_type'] = ''

错误示例:

df.loc[df["bWendu"]-df["yWendu"]>10]["wencha_type"] = "温差大"
df.loc[df["bWendu"]-df["yWendu"]<=10]["wencha_type"] = "温差正常"

两个[]的链式操作相当于

df.get(condition).set(wen_cha)

这儿get得到的成果可能是view也可能是copy,存在歧义

正确演示:

df.loc[df["bWendu"]-df["yWendu"]>10, "wencha_type"] = "温差大"
df.loc[df["bWendu"]-df["yWendu"]<=10, "wencha_type"] = "温差正常"

五、聚合查询

1. describe输出计算成果

# 一下子提取一切数字列计算成果
df.describe()
           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

describe只能得到数值列的计算成果

2. 非数值列计算

2.1 unique仅有去重

print(df['tianqi'].unique())
['晴~多云' '阴~多云' '多云' '阴' '多云~晴' '多云~阴' '晴' '阴~小雪' '小雪~多云' '小雨~ 阴' '小雨~雨夹雪'
 '多云~小雨' '小雨~多云' '大雨~小雨' '小雨' '阴~小雨' '多云~雷阵雨' '雷阵雨~多云' '阴~ 雷阵雨' '雷阵雨'
 '雷阵雨~大雨' '中雨~雷阵雨' '小雨~大雨' '暴雨~雷阵雨' '雷阵雨~中雨' '小雨~雷阵雨' '雷 阵雨~阴' '中雨~小雨'
 '小雨~中雨' '雾~多云' '霾']

2.2 value_counts按值计数

print(df['wencha_type'].value_counts())
温差正常    187
温差大     178
Name: wencha_type, dtype: int64

3. 协方差和相联系数

print(df.cov(), 'nn', 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

六、缺失值处理

1. 处理方式

Pandas运用这些函数处理缺失值:

  • isnull和notnull:检测是否是空值,可用于df和series
  • dropna:丢弃、删去缺失值
    • axis : 删去行仍是列,{0 or ‘index’, 1 or ‘columns’}, default 0
    • how : 假如等于any则任何值为空都删去,假如等于all则一切值都为空才删去
    • inplace : 假如为True则修正当时df,否则回来新的df
  • fillna:填充空值
    • value:用于填充的值,能够是单个值,或者字典(key是列名,value是值)
    • method : 等于ffill运用前一个不为空的值填充forword fill;等于bfill运用后一个不为空的值填充backword fill
    • axis : 按行仍是列填充,{0 or ‘index’, 1 or ‘columns’}
    • inplace : 假如为True则修正当时df,否则回来新的df

2. 数据清洗示例

Pandas数据剖析学习笔记

2.1 检测空值

# 越过前面两空行
studf = pd.read_excel("../datas/student_excel/student_excel.xlsx", skiprows=2)
# 检测空值
print(studf.isnull())
    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

2.2 dropna示例

# 删去全为空的行和列
studf.dropna(axis=1, how='all', inplace=True)
studf.dropna(axis=0, how='all', inplace=True)
print(studf)
     名字  科目    分数
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.3 fillna示例

# 将空的分数填充为0
# 将空的名字填充为上一个值
studf['分数'].fillna(value=0, inplace=True)
studf['名字'].fillna(method='ffill', inplace=True)
print(studf)
    名字  科目    分数
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

七、数据排序

1. 排序办法

  1. Series的排序:
    Series.sort_values(ascending=True, inplace=False)
    参数阐明:
  • ascending:默认为True升序排序,为False降序排序
  • inplace:是否修正原始Series
  1. DataFrame的排序:
    DataFrame.sort_values(by, ascending=True, inplace=False)
    参数阐明:
  • by:字符串或者List<字符串>,单列排序或者多列排序
  • ascending:bool或者bool的列表,升序仍是降序,假如是list对应by的多列
  • inplace:是否修正原始DataFrame

2. 排序示例

# Series的排序
print(df['aqi'].sort_values())
ymd
2018-09-29     21
2018-10-09     21
2018-09-07     22
2018-09-30     22
2018-10-29     22
             ...
2018-11-14    266
2018-03-13    287
2018-04-02    287
2018-03-14    293
2018-03-28    387
Name: aqi, Length: 365, dtype: int64
# DataFrame的排序
df.sort_values(by=['aqi', 'bWendu'], ascending=[False, True], inplace=True)
print(df[['aqi', 'bWendu']])
            aqi  bWendu
ymd
2018-03-28  387      25
2018-03-14  293      15
2018-03-13  287      17
2018-04-02  287      26
2018-11-14  266      13
...         ...     ...
2018-10-29   22      15
2018-09-30   22      19
2018-09-07   22      27
2018-10-09   21      15
2018-09-29   21      22

八、字符串处理

0. Pandas的str

Pandas的字符串处理:

  1. 运用办法:先获取Series的str特点,然后在特点上调用函数;
  2. 只能在字符串列上运用,不能数字列上运用;
  3. Dataframe上没有str特点和处理办法
  4. Series.str并不是Python原生字符串,而是自己的一套办法,不过大部分和原生str很类似;

1. 根底用法

获取str特点,并调用各种办法,如replace, isnumeric, len

print(df['wencha_type'].str.len())
ymd
2018-03-28    3
2018-03-14    4
2018-03-13    3
2018-04-02    3
2018-11-14    4
             ..
2018-10-29    3
2018-09-30    4
2018-09-07    3
2018-10-09    3
2018-09-29    3
Name: wencha_type, Length: 365, dtype: int64

2. 条件查询

或运用contains, startswith等得到bool的Series做条件查询

print(df.loc[df['tianqi'].str.startswith('多云'), ['tianqi', 'fengxiang']])
           tianqi fengxiang
ymd
2018-03-28   多云~晴        东风
2018-03-14   多云~阴       东冬风
2018-04-02     多云        冬风
2018-11-14     多云        南风
2018-11-26     多云       东南风
...           ...       ...
2018-01-25     多云       东冬风
2018-10-10   多云~晴       西冬风
2018-02-03     多云        冬风
2018-09-30     多云       西冬风
2018-10-09   多云~晴       西冬风

3. 正则表达式

由于Series.str天然支撑正则表达式,示例如下:

匹配字符集合并做替换:

# 增加新列
def get_nianyueri(x):
    year,month,day = x["ymd"].split("-")
    return f"{year}{month}{day}日"
df["中文日期"] = df.apply(get_nianyueri, axis=1)
# 尝试将 年 月 日 去除
df.loc[:, '中文日期'] = df['中文日期'].str.replace('[年月日]', '')
print(df['中文日期'])
86     20180328
72     20180314
71     20180313
91     20180402
317    20181114
         ...
301    20181029
272    20180930
249    20180907
281    20181009
271    20180929
Name: 中文日期, Length: 365, dtype: object

捕获组提取数据:

extracted_fengli = df['fengli'].str.extract(r'(d)-(d)')
print(extracted_fengli.head())
     0  1
86   1  2
72   1  2
71   1  2
91   1  2
317  1  2
..  .. ..
301  3  4
272  4  5
249  3  4
281  4  5
271  3  4

九、索引

0. 索引的作用

挑选恰当的索引能够加速查询功能

  1. 当索引是仅有的时,Pandas会用哈希表优化功能,时刻复杂度为O(1)
  2. 当索引不仅有,可是单调时,Pandas会运用二分查找,时刻复杂度为O(log n)
  3. 当索引既不仅有且不单调时,Pandas只能遍历,时刻复杂度为O(n)

因此,我们要判别当时索引是否为以上类型,尽可能挑选仅有的索引,单调次之

1. 挑选索引示例

原始数据如下:

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

判别每一列是否存在仅有束缚:

print(df.nunique() == len(df))
userId       False
movieId      False
rating       False
timestamp    False
dtype: bool

判别每一列是否单调:


# 运用这一句会报FutureWarning
# is_monotonic = df.apply(lambda x: x.is_monotonic)
is_monotonic_increasing = df.apply(lambda x: x.is_monotonic_increasing)
is_monotonic_decreasing = df.apply(lambda x: x.is_monotonic_decreasing)
print(is_monotonic_increasing, 'nn', is_monotonic_decreasing)
userId        True
movieId      False
rating       False
timestamp    False
dtype: bool
 userId       False
movieId      False
rating       False
timestamp    False
dtype: bool

2. 设置索引示例

DataFrame.set_index(keys, append=False, drop=True, inplace=False)

keys代表被用作索引的列

append代表是否保存本来的索引

drop表明是否将指定的列在原数据列中删去

inplace表明是否在原数据上修正

df.set_index('userId',append=True, drop=False, inplace=True)
print(df.head())
          userId  movieId  rating  timestamp
  userId
0 1            1        1     4.0  964982703
1 1            1        3     4.0  964981247
2 1            1        6     4.0  964982224
3 1            1       47     5.0  964983815
4 1            1       50     5.0  964982931