Pandas数据分析学习笔记

前言

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

参考资料:

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

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

一、数据读取

0. 数据类型

数据类型说明Pandas读取方法
csv, tsv, txt用逗号、tab或其它字符分割的文本文件read_csv
excelxls或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']
)

分割符也可以是多个字符,但是seq指定多个字符默认会被当作正则表达式,需指定engine=‘python’


df_ratings = pd.read_csv(
    "../datas/movielens-1m/ratings.dat",
    sep="::",
    engine='python',
    names="UserID::MovieID::Rating::Timestamp".split("::")
)

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, '\n\n', df.columns, '\n\n', 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. Series.map

传入一个字典,将Series的值根据字典映射

示例如下:

原始数据

stocks = pd.read_excel('../datas/stocks/互联网公司股票.xlsx')

print(stocks.head(), '\n', stocks['公司'].unique())
           日期    公司      收盘      开盘       高       低    交易量   涨跌幅
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
 ['BIDU' 'BABA' 'IQ' 'JD']

现将 公司 这一列通过字典对应到该公司的中文名

dict_company_names = {
    "bidu": "百度",
    "baba": "阿里巴巴",
    "iq": "爱奇艺",
    "jd": "京东"
}

stocks['公司中文'] = stocks['公司'].str.lower().map(dict_company_names)

print(stocks['公司中文'].unique())
['百度' '阿里巴巴' '爱奇艺' '京东']

其实map也可以像apply一样传一个函数,修改Series的每个值

5. 条件选择分组后赋值

# 先创建空列(这是第一种创建新列的方法)
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(), '\n\n', 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

4. 分组后聚合查询

4.1 pd.groupby

DataFrame.groupby(by, axis=0, as_index=True, sort=True, group_keys=True,  dropna=True)

  • by: 字符串或者List<字符串>,用于指定分组
  • axis:默认为0,对行做分组
  • as_index:将分组的keys作为索引值,默认为True
  • sort:将结果按分组的keys排序,默认为True
  • group_keys:与as_index相同,但是只有在使用apply时生效,在使用聚合时失效(都为True)

DataFrame.groupby的返回值是一个DataFrameGroupBy对象

4.2 聚合查询示例

1) 使用已有的聚合函数

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)})
     A      B         C         D
0  foo    one  0.411257 -1.345015
1  bar    one  0.680037  0.225904
2  foo    two -0.640561 -0.447943
3  bar  three -0.354500 -0.517335
4  foo    two  1.092376 -0.978323

agg列表传参,对不同列使用相同聚合函数:

df_group1 = df.groupby(['A', 'B']).agg([np.sum, np.mean])
                  C                   D
                sum      mean       sum      mean
A   B
bar one    0.680037  0.680037  0.225904  0.225904
    three -0.354500 -0.354500 -0.517335 -0.517335
    two   -1.047294 -1.047294 -1.879435 -1.879435
foo one    0.419079  0.209540 -1.893515 -0.946758
    three -0.681836 -0.681836  1.008956  1.008956
    two    0.451816  0.225908 -1.426266 -0.713133

agg对象传参,对不同列使用不同聚合函数:

df_group2 = df.groupby(['A', 'B']).agg({'C': np.sum, 'D': np.mean})
                   C         D
A   B
bar one    0.680037  0.225904
    three -0.354500 -0.517335
    two   -1.047294 -1.879435
foo one    0.419079 -0.946758
    three -0.681836  1.008956
    two    0.451816 -0.713133

DataFrameGroupBy对象原生的函数:

df_group3 = df.groupby('A')['C'].mean()
A
bar   -0.240586
foo    0.037812
Name: C, dtype: float64

2)自定义聚合函数

DataFrameGroupBy.apply(func,  *args,  **kwargs)

  • func:自定义的聚合函数,第一个参数是一个DataFrame
  • *args, **kwargs:func第二个及往后的参数(如有)

使用的数据为多次出现在例子中的天气数据,增加一列为月份,由ymd年月日得到:

# 新增一列为月份
df['month'] = df['ymd'].str[:7]
          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

对月份做分组,并对分组后的数据对最高温度做排序,返回最高温度最大的3行

def getTopN(df, n):
    return df.sort_values(by='bWendu')[['ymd', 'bWendu', 'yWendu']][-n:]

df_group = df.groupby('month').apply(getTopN, 3)
                   ymd  bWendu  yWendu
month
2018-01 16  2018-01-17       6      -7
        13  2018-01-14       6      -5
        18  2018-01-19       7      -4
2018-02 58  2018-02-28       9      -2
        53  2018-02-23      10      -4

六、缺失值处理

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. 数据清洗示例

image.png

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, '\n\n', 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

十、数据合并

1. merge合并

1.1 函数定义

课件中给出的pd.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)

  • left,right:要merge的dataframe或者有name的Series
  • how:join类型,‘left’, ‘right’, ‘outer’, ‘inner’
  • on:join的key,left和right都需要有这个key
  • left_on:left的df或者series的key
  • right_on:right的df或者seires的key
  • left_index,right_index:使用index而不是普通的column做join
  • suffixes:两个元素的后缀,如果列有重名,自动添加后缀,默认是(‘_x’, ‘_y’)

如果学过SQL语句,可以很好地将两者对应起来:

pd.merge(left, right, on=‘key’)
相当于
FROM left JOIN right ON left.key=right.key

pd.merge(left, right, left_on=‘key1’, right_on=‘key2’)
相当于
FROM left JOIN right ON left.key1=right.key2

而how=‘left’/‘right’/'outer’就相当于LEFT/RIGHT/FULL JOIN

1.2 合并示例

df_ratings = pd.read_csv(
    "../datas/movielens-1m/ratings.dat",
    sep="::",
    engine='python',
    names="UserID::MovieID::Rating::Timestamp".split("::")
)
df_users = pd.read_csv(
    "../datas/movielens-1m/users.dat",
    sep="::",
    engine='python',
    names="UserID::Gender::Age::Occupation::Zip-code".split("::")
)

print(df_ratings.head(), '\n', df_users.head())

df_joined1 = pd.merge(df_ratings, df_users, on='UserID')

print(df_joined1.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
   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

2. concat合并

2.1 函数定义

pandas.concat(objs, axis=0, join=‘outer’, ignore_index=False)

  • objs:一个列表,内容可以是DataFrame或者Series,可以混合
  • axis:默认是0代表按行合并,如果等于1代表按列合并
  • join:合并的时候索引的对齐方式,默认是outer join,也可以是inner join
  • ignore_index:是否忽略掉原来的数据索引

DataFrame.append(other, ignore_index=False)

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

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

2.2 合并示例

原始数据:

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, '\n', df2)
    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

有无ignore_index的区别:

df3 = pd.concat([df1, df2])

df4 = pd.concat([df1, df2], ignore_index=True)

print(df3, '\n', df4)
    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
     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

默认为outer,使用inner如下:

df5 = pd.concat([df1, df2], join='inner')

print(df5)
    A   B   C   D
0  A0  B0  C0  D0
1  A1  B1  C1  D1
2  A2  B2  C2  D2
3  A3  B3  C3  D3
0  A4  B4  C4  D4
1  A5  B5  C5  D5
2  A6  B6  C6  D6
3  A7  B7  C7  D7

十一、数据透视

1. 透视方法

1) stack 和 unstack

stack和unstack互为逆操作

stack将列索引转成最内层行索引

而unstack将最内层行索引转成列索引

具体参考:pandas中DataFrame的stack()、unstack()和pivot()方法的对比_pandas pivot和stack区别-CSDN博客

2) reset_index 和 pivot

DataFrame.pivot(index, columns, values)

指定三列,依次为行索引、列索引、构建一个DataFrame

使用rest_index可实现逆操作

2. 透视示例

2.1 数据预处理

原始数据:

df = pd.read_csv(
    "../datas/movielens-1m/ratings.dat",
    header=None,
    names="UserID::MovieID::Rating::Timestamp".split("::"),
    sep="::",
    engine="python"
)

print(df.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

调用pd.to_datetime将时间戳转化为时间对象,通过访问对象的属性dt.month访问月份,并做统计操作

# 统计每个月份每个评级的用户数量

df["pdate"] = pd.to_datetime(df["Timestamp"], unit='s')
df_group = df.groupby([df['pdate'].dt.month, 'Rating'])['UserID'].count()

print(df_group.head())
pdate  Rating
1      1         1127
       2         2608
       3         6442
       4         8400
       5         4495
Name: UserID, dtype: int64

2.2 使用透视

1) unstack 和 stack示例

df_unstack = df_group.unstack()

print(df_unstack.head())
Rating     1     2      3      4      5
pdate
1       1127  2608   6442   8400   4495
2        629  1464   3297   4403   2335
3        466  1077   2523   3032   1439
4       1048  2247   5501   6748   3863
5       4557  7631  18481  25769  17840

绘制曲线

df_unstack.plot()
plt.show()

image.png

2) reset_index 和 pivot示例

df_reset = df_group.reset_index()

print(df_reset.head())
   pdate  Rating    pv
0      1       1  1127
1      1       2  2608
2      1       3  6442
3      1       4  8400
4      1       5  4495
df_pivot = df_reset.pivot('pdate', 'Rating', 'pv')

print(df_pivot.head())
Rating     1     2      3      4      5
pdate
1       1127  2608   6442   8400   4495
2        629  1464   3297   4403   2335
3        466  1077   2523   3032   1439
4       1048  2247   5501   6748   3863
5       4557  7631  18481  25769  17840
  • 35
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值