Python之数据分析

Numpy

Numpy是Python的一种开源的数值计算扩展。可以用来存储和处理大型矩阵,比Python自身的嵌套列表结构(nested list structure)高效得多。在实际工作中直接使用情况较少

  • 数组

    numpy.array()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import numpy as np
    a = np.array([1, 2, 3, 4])
    print(a)

    # 输出为:array([1, 2, 3, 4])
    # 和 列表 相似,但处理效率要高很多

    a[0] = 5
    # 得到数组 array([5, 2, 3, 4])

    a + 1
    # 数组中每个元素都+1,得到数组 array([6, 3, 4, 5])
    # 减法、乘除法同理

    b = np.array([[1, 2, 3], [4, 5, 6]])
    # 可以生成多维数组
    • 查看数组中存储的数据类型dtype

      1
      2
      print(b.dtype)
      # 输出为:int32

Pandas

Pandas是基于Numpy的一种工具,是为了解决数据分析任务而创建的。Pandas提供了大量能使我们快速便捷地处理数据函数和方法

1
import pandas as pd

两种数据结构

Series (一维)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
s1 = pd.Series([1, 2, 3, 4])
print(s1)
# 输出为:
# 0 1
# 1 2
# 2 3
# 3 4
# dtype: int64

print(s.index)
# 输出为:RangeIndex(start=0, stop=4, step=1)

s1[1]

s2 = pd.Series([1, 2, 3, 4], index = ['a', 'b', 'c', 'd'])
print(s2)
# 输出为:
# a 1
# b 2
# c 3
# d 4
# dtype: int64

s2['b']

print(s2[['a', 'c']])
# 索引多个值 需要用 列表 来表示
# 输出为:
# a 1
# c 3
# dtype: int64

d = {'hunter':'smart', 'peter':'handsome'}
s3 = pd.Series(d)
print(s3)
# 输出为:
# hunter smart
# peter handsome
# dtype: object

s3 = pd.Series(d, index=['hunter', 'peter', 'haha'])
print(s3)
# 输出为:
# hunter smart
# peter handsome
# haha NaN
# dtype: object

# 字符串类型在Series里显示的类型为object
# 可转换类型
s2 = s2.astype('str') # 将 数值类型 转换为 字符串类型

# 将浮点数类型转换为int类型
x_value = titanic['Fare'].astype(int)


DataFrame (二维)

1
2
3
4
5
6
7
8
9
10
d = {
'name':['qinlu', 'lulu', 'qinqin']
'sex':['male', 'male', 'female']
'age':[18, 18, 25]
}

pd.DataFrame(d)

# 创建一个空的数据框
data = pd.DataFrame()

得到如下二维表格:

name sex age
0 qinlu male 18
1 lulu male 18
2 qinqin female 25
1
2
df = pd.DataFrame([1, 2, 3, 4]) # 效果类似 Series
df

得到如下二维表格:

0
0 1
1 2
2 3
3 4

1
2
3
4
df = pd.DataFrame([[1, 2, 3, 4], [3, 4, 5, 6]], columns=list('abcd'))
# 提供两行数据和 各列的列名

df

得到如下二维表格:

a b c d
0 1 2 3 4
1 3 4 5 6

DataFrame的列的顺序默认为提供的顺序。如下操作可以调整列的顺序

1
2
df = df[['b', 'd', 'c', 'a']]
df

得到如下二维表格:

b d c a
0 2 4 3 1
1 4 6 5 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
df = pd.DataFrame(d)

# 概览数据
df.info()
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 3 entries, 0 to 2
# Data columns (total 3 columns):
# # Column Non-Null Count Dtype
# --- ------ -------------- -----
# 0 name 3 non-null object
# 1 sex 3 non-null object
# 2 age 3 non-null int64
# dtypes: int64(1), object(2)
# memory usage: 200.0+ bytes

# 查看每个列的数据类型
df.dtypes

# 查看某个列的数据类型
df['name'].dtype

#获取某一行
df.iloc[0] #根据 索引(不论是否自定义了标签名) 读取某一行 (推荐,数据量大了之后更适用)
df.loc[0] # 根据 具体标签名 读取某一行
# 上述两种方式输出相同:
# name lulu
# sex male
# age 18
# Name: 1, dtype: object

# 获取多行
df.iloc[0:2] # 闭区间
df[0:2] # 开区间

#获取某一列
df['age']

#获取多个列
df[['age', 'name']]
df.iloc[:, 0:7] # 所有行和前七列

# 获取二维表格部分列
df[['age', 'name']][1:2] # 开区间
df[['age', 'name']]['age'] # 获取部分列中的指定列

# 读取二维表格部分区域
df.iloc[1:2, 1:2] # 指出 索引,iloc不支持布尔类型的过滤
df.loc['a', ['age', 'name']] # 指出 行和列的 标签名
df.loc[df.age == 19, 'age'] # 指出 符合条件的行的列值 和 列标签

# 更改具体值
df['age'] = 21 # 统一更改整个列
df['age'] += 1 # 统一更改整个列
df['age'] = [21, 18, 18] # 自定义更改列中的值
df['age'][1] = 22 # 更改某个值
df.loc[df.age == 19, 'age'] = 20 # 更改某个筛选范围的值

# 更改标签
df.index = list('abc') # 更改行标签,通常处理的数据量很大,不建议修改
df.columns = list('abcd') # 更改列标签

Series通过reset_index()可以转换为DataFrame

Series原有的index也是变为一个列


~代表反转

1
~ (df.age > 19) #等价于 df.age <= 19

筛选

1
2
3
4
5
6
df[df.age == 18] # 筛选出 年龄=18的 所有行(筛选 结果为True 的行)
df.query('age == 18') # 和上一条代码等价,条件需要引号标注

df[(df.age == 18) | (df.name == 'qinqin')] # 通过 括号和&/|运算符 进行多条件筛选
df.query("(age == 18) | (name == 'qinqin')") # 注意括号不要冲突


增删

pandas中增删的运算效率低,尽量不使用。

  • del:将指定列从存储空间中删除
1
del df.loc[df.age == 19, 'age']
  • drop():删除指定项时返回的是视图数据仍然在存储空间中

    1
    2
    df.drop('age', axis=1)
    # axis=1表示列,axis=0表示行(默认)

Pandas基础命令速查表


读取/写入文件

读取

1
2
3
4
5
import pandas as pd

df = pd.read_csv('xxxx.csv', encoding='xxx', sep=',', names='abcdefg')

df = pd.read_table('xxxx', sep='\t')

read_csv()的参数:

  • 文件名

  • encoding:指定编码

    python默认以utf-8编码读取文件。如果报错无法正确显示文件内容,可以将编码改为gbk等进行读取。

  • sep:指定分隔符,默认为逗号,

    有的文件由于不标准等原因,采用其他分隔符如\t,可通过指定分隔符优化显示

    sep='\s+':将tab多个空格当成一样的分隔符

  • parse_dates:将数据解析为日期

    parse_dates = True尝试解析所有可能为日期类型的列;

    parse_dates = [1, 2]尝试解析给定列为日期类型的列。

    parse_dates = [[1, 2]]尝试解析给定列为日期类型的列,并将这些列聚合成为1个列

  • names

    指定列名默认为文件中的第一行。如果自定义列名,文件中的第一行将作为第一行数据显示

1
2
3
4
5
6
7
df.info() # 概览数据

df.head(10) # 默认读取前5行
df.tail(20) # 默认读取最后5行

# 根据当前已有的列生成新列
df['avg'] = (df.bottom + df.top) / 2

写入

to_csv()


函数

  • T转置表格

    df.T

  • shape:获取数据框的行数列数元组存储)

    df.shape:可能得到如(10,5)(10行5列)

  • columns:获取所有列

  • index:查看索引信息

  • set_index()设置索引

    1
    crime = crime.set_index('Year')

筛选函数

  • sort_values()按值排序,默认升序

    参数:

    • by:指出排序依据的字段

      1
      2
      3
      4
      5
      6
      7
      df.sort_values(by = 'avg')

      # 下述方式效果类似(返回的是avg的有序数组)
      df.avg.sort_values()

      # 根据多个条件排序
      df.sort_values(by = ['avg', 'city']) # 排序根据unicode而不是拼音,如果想要按照拼音顺序,需要将中文和英文字母关联(可新建一个城市首字母缩写的列)
    • ascending:默认为True(升序)

    • inplace:是否用排序后的数据集替换原来的数据默认为False

  • sort_index()按索引排序

  • rank():给出排名

    参数:

    • ascending:默认为True(升序)

    • method

      • 'average' (默认,如前5个人分数相同,则排名为**(1+5) / 2** (最大值和最小值的加权平均数))
      • 'min' (取最小值)
      • 'max'
      • 'first' (不考虑并列情况)
    1
    df['rank'] = df.avg.rank(ascending=False, method='min') # 按照平均工资排名,将排名作为新的列加入表格
  • unique()去除重复

    1
    df.city.unique()
  • value_counts():对Series的每个值进行计数并且排序

    1
    df.education.value_counts()
  • describe()

    1
    df.avg.describe()

    得到:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    count 	5031.000000
    mean 17.111409
    std 8.996242
    min 1.500000
    25% 11.500000
    50% 15.000000
    75% 22.500000
    max 75.000000
    Name: avg dtype: float64
    • count:计数
    • mean:平均数
    • std标准差

    describe()的统计内容会自动忽略空值

  • count()

  • max()

  • min()

  • last()

  • mean()

    整个数据框求平均值,结果为按列(默认)分别计算平均值,得到一个Series

    对Series求平均值,结果则为一个平均数

    参数:axis=0 (0为按列,1为按行)

  • sum()

  • median()中位数

  • std()标准差

  • var()方差

  • cumsum()累加

  • pandas.cut()分类统计

    1
    2
    3
    4
    5
    6
    # 4等分
    pd.cut(df.avg, bins=4, label=list('abcd'))

    # 自定义分隔区间
    # 为了能够全部包含需要分类的数据区间,最后的值一般一个取极大的值
    pd.cut(df.avg, bins=[0, 5, 10, 20, 30, 100], label=['0~5', '5~10', '10~20', '20~30', '30~100'])
  • pandas.qcut()

  • isin()

    1
    2
    # 找到英格兰(England)、意大利(Italy)和俄罗斯(Russia)的射正率(Shooting Accuracy)
    euro12.loc[euro12['Team'].isin(['England', 'Italy', 'Russia']), ['Team', 'Shooting Accuracy']]

    可以和~配合使用,达到不存在的函数isnotin()的效果

  • idxmax():返回请求轴第一次出现最大值(不包括``NA/null`)的索引

    参数:

    • axis=0:0对应行,1对应列

聚合函数

MySQL难以完成分组排序

  • groupby():分组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    df.groupby(by = 'city')
    # 得到如下已存入内存的提示:
    # <pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000012CF16A41C8>

    # 当前分组下,对各分组中的各列进行统计
    df.groupby(by = 'city').count()

    # 当前分组下,各分组中各列的最大值
    df.groupby(by = 'city').max()

    # 当前分组下,对各分组中某列的最大值
    df.groupby(by = 'city')['avg'].max()

    # 多字段分组
    df.groupby(by = ['city', 'workYear']).mean()


    for k, v in df.groupby(by=['city']):
    # 打印每个分组中的元素数量
    print(len(k[1]))

    # 打印分组的依据元素(具体的city)
    print(k)

    # 打印分割线
    print('**' *10) # 20个星号

    # 打印分组中元素的详细内容
    print(v)

    # 打印同个城市中,最高薪资和最低薪资的差值
    print(max(v.avg) - min(v['avg']))

多表关联
1
2
3
4
import pandas as pd

position = pd.read_csv('position.csv')
company = pd.read_csv('company.csv')
  • merge()函数 —— 根据具体的键值

    相当于SQL中的JOIN

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    position.merge(right = company, how='left', on='companyId')

    # 更改列名

    # 方式一:单独更改一个列名
    col = list(company.columns)
    col[0] = 'id'
    company.columns = col

    # 方式二:利用rename()更改指定列名
    company.rename(columns={'0':'id', '1':'xxx'}, inplace=True)

    pd.merge(left=position, right=company, how='inner', left_on='companyId', right_on='id')

    参数:

    • left/right:关联的表

    • how关联的方式

      • inner(默认)
      • outer
      • left
    • right

    • on:具体的键值(在两个表中对应列名相同时使用)

    • left_on/right_on:具体的键值(在两个表中对应列名不同时使用,关联之后保留不同名的列)


  • join():根据索引(行号)

    按照索引关联很有局限性

    • 只要两个表列名不同,不加任何参数就可直接使用
    • 如果两个表有重复的列名,需要制定lsuffixrsuffix参数
    • 默认左外连接(LEFT JOIN)
    1
    df1.join(df2)
  • concat()堆叠

    类似于SQL中的UNION,多个表暴力堆叠,新的表包含多个表的所有列,没有对应列值的表项为NaN。

    参数:

    • axis:
      • 0上下堆叠(默认相同列名合并 )
      • 1左右堆叠
    1
    pd.concat([company, position], axis=1)

区分辨别

Series级别可以直接输入索引标签进行筛选;DataFrame级别,需要通过loc进行筛选。

1
2
3
4
5
6
position.groupby(by=['city', 'education']).mean().avg['上海']

position.groupby(by=['city', 'education']).mean().loc['上海', '博士']

# reset_index() 重置索引
position.groupby(by=['city', 'education']).mean().reset_index()['上海', '博士']

不借助groupby,如何设置多重索引

1
2
# sort_values 用于排序,set_index 将列设置为索引
position.sort_values(by=['city', 'education']).set_index(['city', 'education'])

  • agg()函数

    agg()是聚合函数,得到DataFrame。参数有:

    • func实现某种统计功能的函数
    • axis=0
    1
    2
    3
    # grouped_user 是通过groupby('user_id')得到的 DataFrameGroupBy 对象
    user_life = grouped_user['order_dt'].agg(['first', 'last']) # order_dt列是时间格式,first 和 last 函数能分别得到最早和最迟的时间
    user_life.head()

文本函数

  • str.count():统计指定字符出现的次数

    1
    position.positionLabels.str.count('分析师')
  • str.find():查找指定字符出现的位置

    1
    position.positionLabels.str.find('数据')
  • str[1:-1]:去除表示列表的方括号

    1
    position.positionLabels.str[1:-1]
  • str.replace("'", "")去除列表中元素的单引号

    1
    position.positionLabels.str[1:-1].str.replace("'", "")
  • str.startwith():筛选以指定字符开头的字符串

    1
    euro12['Team'].str.startwith('G')
  • str.split()拆分单元格内容

    1
    2
    3
    df2 = df['某一列'].str.split('指定分隔符', expand=True)
    # 默认会删去分隔符
    # expand=True 能让拆分后的单元格内容 由 列表类型 变为 DataFrame(默认为False)
  • str.extract()提取单元格中所需内容

    参数:

    • pat:字符串或正则表达式
      • 只有用()包裹的部分才会被保留
      • Python中使用正则表达式命名捕获组语法为:(?P<name>Expression),即name作为列名
    • flags:整型
    • expand:是否将提取的内容由列表类型变为DataFrame
    1
    2
    # 提取 '建筑年代'列 的数字,剥离 年建
    df3['建筑年代'] = df3['建筑年代'].str.extract('(\d+)年建')
  • str(xxx):将其他类型的数据(如时间)转为字符串


时间相关

  • Python中的 datetime模块

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    from datetime import datetime
    from datetime import timedelta

    now = datetime.now() # 获取当前日期和时间
    print(now)

    print(datetime.date.today()) # 获取当前日期

    delta = now - datetime(2017,6,27,10,10,10,10) # 获取日期相差值
    print(delta) # 得到 xxdays, xx:xx:xx.xxx

    print(delta.days) # 72天数

    print(delta.seconds) # 秒数
    print(delta.microseconds) # 微秒数


    # 2061年?我们真的有这一年的数据?创建一个函数并用它去修复这个bug
    # x是一个 YYYY-mm-dd格式 的日期
    def fix_century(x):
    year = x.year
    if year > 2000:
    year -= 100
    return datetime.date(year, x.month, x.day)


    text = '2019-09-07'
    y = datetime.strptime(text, '%Y-%m-%d') # 把字符串转为日期
    print(y)

    # 当前日期的前后n日期
    print(datetime.date.today()+timedelta(days=-1))

    # 获得某一日期的月初和月末
    text='2019-09-07'
    month_first=datetime.strptime(text[:8]+'01','%Y-%m-%d')
    print(month_first) #输出结果为:2019-09-01 00:00:00

    month_end=datetime.strptime(text[:5] + str(int(text[5:7])+1) + '-01','%Y-%m-%d')+timedelta(days=-1)
    print(month_end) # 输出结果为:2019-09-30 00:00:00
    • datetime.now():获取当前日期和时间(和MySQL的NOW()相同)
    • datetime.date.today():获取当前日期
    • datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]]):生成指定的时间
    • datetime.date(year, month, day):构造日期格式,但是数据类型不变
    • datetime.strptime():把字符串转为日期(和pandas的to_datetime函数作用相同)

  • Pandas中的 to_datetime函数

    pandas.to_datetime()将指定列的 数据类型 转换为 指定格式的 时间类型

    1
    2
    3
    4
    5
    6
    7
    import pandas as pd

    crime['Year'] = pandas.to_datetime(crime['Year'], format='%Y')

    date=['2017-6-26', '2017-6-27']
    print(pd.to_datetime(date))
    #输出结果为:DatetimeIndex(['2017-06-26', '2017-06-27'], dtype='datetime64[ns]', freq=None)

    format参数数据可视化-模块 datetime 含义一致


  • astype()

    可以将时间类型的数据更改精度pandas.to_datetime()默认转换的时间类型datetime64[ns]精度为ns(纳秒)级

    1
    2
    df['datetime'] = pd.to_datetime(df['datetime'], format='%Y%m%d')
    df['month'] = df['datetime'].values.astype('datetime64[M]') # 将精度转换为M(月份)级别,日子一律会更改为1日

    上述代码,使用values将Series内的数值以ndarrayndarray-like的形式返回,因为datetime64[ns]无法直接转换为datetime64[M]类型


  • pandas.date_range()创建时间序列

    参数:

    • start:起始时间
    • end:结束时间
    • periods:当只声明了起始时间结束时间时,需要告知时间范围
    • freq时间频率(默认为'D'(Calendar day frequency))
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import pandas as pd
    from datetime import datetime

    pd.date_range(start=datetime.now(), periods=6, freq='B') # 'B'的含义为 营业日频率(business day frequency)
    # 得到:
    # DatetimeIndex(['2020-07-13 20:34:18.990072', '2020-07-14 20:34:18.990072',
    # '2020-07-15 20:34:18.990072', '2020-07-16 20:34:18.990072',
    # '2020-07-17 20:34:18.990072', '2020-07-20 20:34:18.990072'],
    # dtype='datetime64[ns]', freq='B')


    pd.date_range(start='20130809',periods=8,freq='SM') # 'SM'的含义为 月中和月末(semi-month end frequency)
    # 得到:
    # DatetimeIndex(['2020-07-15', '2020-07-31', '2020-08-15', '2020-08-31',
    # '2020-09-15', '2020-09-30', '2020-10-15', '2020-10-31'],
    # dtype='datetime64[ns]', freq='SM-15')


    pd.date_range(start=datetime.now(),periods=5,freq='2H20min') # 时间间隔140min

    pd.date_range(start='20190623', periods=10, freq='1D10U') # 时间间隔1天10微秒

  • numpy.timedelta64()

    Numpy允许两个Datetime值相减,这个操作产生一个带有时间单位的数字。由于NumPy的核心没有物理量(物理单位)系统,因此**创建了timedelta64数据类型以补充datetime64**。

    1
    2
    3
    4
    5
    x = np.datetime64('2017-08-03') - np.datetime64('2017-07-15')
    # 得到的结果是 x = numpy.timedelta64(19,'D') # 第一个参数为数字,第二个参数为单位

    # 利用timedelta64() 消除单位
    x / np.timedelta64(1, 'D') # 得到的结果是19.0

  • 以日期时间序列为索引的最多日期相差天数

    1
    (df.index.max() - df.index.min()).days
  • 以日期时间序列为索引的数据框中一共有多少个月

    1
    2
    d = df.resample('BM').last()
    len(d.index) # 'DatetimeIndex' object has no attribute 'count',因此不能使用count函数计数
  • 把字符串转成日期


重新采样

重新采样指将时间序列从一个频率转换为另一个频率的过程。

  • 采样(向下采样)

    高频时间序列变为低频,时间粒度变大。如:原有100个时间点,变为10个时间点

  • 升采样(向上采样)

    低频时间序列变为高频,时间粒度变小。,

  • 同频之间的切换

    比如W-WED(weekly on Wednesday 每周三)转换到W-FRI(每周五)

**函数resample()**:

不同于groupby关注特征值的分组操作,它在以时间序列为索引的数据框中使用,对时间索引分组操作来聚合运算。

参数:

  • rule:所需采样频率的字符串

    字符串 含义
    B 营业日频率
    (business day frequency)
    C 自定义营业日频率
    (custom business day frequency)
    D 日历日频率
    (calendar day frequency)
    W 每周一次频率
    (weekly frequency)
    M 月末频率
    (month end frequency)
    SM 月中和月末频率
    (semi-month end frequency (15th and end of month))
    BM 营业月末频率
    (business month end frequency)
    CBM 自定义月末频率
    (custom business month end frequency)
    MS 月初频率
    (month start frequency)
    SMS 月初和月中频率
    (semi-month start frequency (1st and 15th))
    BMS 营业月初频率
    (business month start frequency)
    CBMS 自定义营业月初频率
    (custom business month start frequency)
    Q 季度末频率
    (quater end frequency)
    BQ 营业季度末频率
    (business quater end frequency)
    QS 季度初频率
    (quater start frequency)
    BQS 营业季度初频率
    (business quater start frequency)
    A / Y 年末频率
    (year end frequency)
    BA / BY 营业年末频率
    (business year end frequency)
    AS / YS 年初频率
    (year start frequency)
    BAS / BYS 营业年初频率
    (business year start frequency)
    BH 营业小时频率
    (business hour frequency)
    H 小时频率
    (hourly frequency)
    T / min 分钟频率
    (minutely frequency)
    S 秒频率
    (secendly frequency)
    L / ms 毫秒频率
    (milliseconds)
    U / us 微秒频率
    (microseconds)
    N 纳秒频率
    (nanoseconds)
  • axis=0:需要采样的轴向(0为行方向)

  • closed:在采样时,各时间段哪一侧(left/right)是闭合的。

  • label:在降采样时,index用区间的左界值还是右界值


空值相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd
import numpy as np

--snip--
# 赋空值
# NaN是C语言类型的空值,比直接赋值None更合适
position.loc[position.city=='深圳', 'city'] = np.NaN

# 统计数据框中每列缺失值的数量(每列缺失的行数)
position.isnull().sum()

# 统计数据框中每列有多少非空值(非空行)
# 方式一:
position.notnull().sum
# 方式二:
position.shape[0] - position.isnull().sum()


# 对空值进行填充
position.fillna(1, inplace=True) # 统一填充

# 删除空值所在的行(默认)或列
position.dropna()

去重

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 查找重复值(默认:第一次遇到不会当作重复值,将第二次开始遇到的值作为重复值看待)
position[position.duplicated()]

s = pd.Series([1, 1, 2, 4, 5])
# 判断是否有重复
s.is_unique

s[s.duplicated()] # 会筛选出索引为1处对应的值1,索引为0处的1不会被筛选出来
s[s.duplicated(keep='last')] # 保留最后一个重复的值,即索引为0处的1会被筛选出来

# 去重
s.drop_duplicates() # 默认保留第一个重复值(keep=’first')
# 获取每月的消费人数 (需要根据user_id去重)
# 方式一
plt.plot(df['user_id'].apply(lambda x:len(x.drop_duplicates())))
# 方式二
d = df.groupby(['month', 'user_id']).count().reset_index()
plt.plot(d.groupby('month')['user_id'].count())


# 返回去重后的列
s.unique()

# 返回去重后的元素个数
s.nunique()

移动数据:shift()

参数:

  • periods:移动的幅度(可正可负),默认为1

    只移动数据,不移动索引,移动之后没有对应值的,赋值为``NaN`

  • freq:用于时间序列索引默认为None

    • DateOffset
    • timedelta
    • time rule string

    按照参数值移动时间索引不移动数据值

  • axis:指定移动方向

    • 0:上下移动(默认)
    • 1:左右移动

apply()

apply()可以将函数应用到所有的行/列上进行处理

  • apply()中的匿名函数``lambda中的x`到底指代什么

    1. 某一列操作时:

      每一个x依次对应各行的具体值

    2. 多个列(axis**=0**,默认)操作时:

      每一个x依次对应每一列

    3. 多个列(axis**=1**)操作时:

      每一个x依次对应每一行(依次对每个单元格进行操作)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Series级别

# 使用匿名函数,向 position表格中的avg列的每一行 都在末尾添加一个字符k
position.avg.apply(lambda x:str(x)+'k')

# 显式创建函数
def func(x):
if x > 20:
return '20+k'
else:
return str(x)+'k'

position.avg.apply(func)
# -----------------------------------------------
# DataFrame级别

# 方式一
def func(x):
if x.avg > 20:
return '20+k'
else:
return '0~20k'

position.apply(func, axis=1) # axis=1应用于每行;axis=0应用于每列

# 方式二
def func(x):
if x > 20:
return '20+k'
else:
return '0~20k'

position.apply(lambda x:func(x.avg), axis=1) # axis=1应用于每行;axis=0应用于每列

  • 匿名函数lambda中条件语句的使用

    1
    2
    3
    # 条件语句为 A if 条件 else B
    # 即 满足条件则输出A,否则输出B
    x.apply(lambda x:'1' if x>= 0 else '0')

apply()应用到聚合函数上

获取不同城市中工资水平前五的信息:

1
2
3
4
5
def func(x, n):
r = x.sort_values('avg', ascending=False) # 按工资降序排序
return r[:n] # 返回前n

position.groupby('city').apply(func, n=3)

Pandas中map(), apply()applymap()的区别

参考:Pandas中的map(), apply()和applymap()的应用

它们的区别在于应用的对象不同

  • map()

    是一个Series的函数,将一个自定义的函数应用于Series结构中的每个元素

  • apply()

    将函数作用于DataFrame中的每个行或者

  • applymap()

    将函数作用于DataFrame中的每个元素


数据透视

pivot_table()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
position.pivot_table(index=['city', 'education'], 
columns='workYear',
values=['avg', 'top'],
margins=True)
# index 指定索引(可多重)
# columns 可选,用于细分数据(相当于Excel透视表里的列标签)
# values 筛选列(相当于Excel透视表里的数值)
# margins=True 对各列进行汇总计算,默认为False
# aggfunc 应用于参数values

position.pivot_table(index=['city', 'education'],
columns='workYear',
values=['avg', 'top'],
aggfunc={'avg':'mean', 'top':np.sum}).reset_index().to_csv('test.csv') # avg求平均值,对top求和

注:数据透视不适合用来去重


连接数据库

  • 安装相关工具包

pip install pymysql

注意:如果是安装了Anaconda,要在Jupyter Notebook中载入pymysql包,则必须在Jupyter Notebook中打开Python3或Ternimal的页面输入上述代码。另外单独安装的Python是独立于Anaconda的,两者下载的工具包可能无法通用。

  • 连接

    1
    2
    3
    4
    5
    6
    7
    8
    conn = pymysql.connect(
    host = 'localhost', # 本地数据库,也可输入 '127.0.0.1'
    user = 'root',
    password = '123456',
    db = 'test',
    port = 3306,
    charset = 'utf8'
    )
  • 查询数据库

    1
    2
    3
    4
    5
    6
    cur = conn.cursor() # 获取游标
    cur.execute('SELECT * FROM courses') # 执行SQL语句
    data = cur.fetchall() # 调取SQL语句执行后的结果(元组的形式)

    for d in data:
    print(d[0], d[1], d[2])
  • 提交对数据库的修改

    1
    conn.commit()
  • 关闭数据库的连接

    1
    2
    cur.close()
    conn.close()

使用Pandas对数据库进行操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import pymysql
import pandas as pd
from sqlalchemy import create_engine

# 读取
def reader(query, db='data'):
sql = query
engine = create_engine('mysql+pymysql://root:123456@localhost:3306/{0}?charset=utf8'.format(db)) # 允许使用pymysql方法连接mysql数据库
df = pd.read_sql(sql, engine) # 将数据库中数据作为DataFrame读取
return df

reader("SHOW TABLES")

df_company = reader("SELECT * FROM company")
df_dataanalyst = reader("SELECT * FROM dataanalyst")
merged = pd.merge(df_dataanalyst, df_company, on='companyId')

result = merged.groupby(['city', 'companyFullName']).count()['positionId'].reset_index()

# 写入
result.to_sql(name='newtable',
con='mysql+pymysql://root:123456@localhost:3306/data?charset=utf8',
if_exists='append'# 表不存在时,创建新表(if_exists默认为fail,表不存在时无法写入),但为了符合数据库中的数据格式要求,最好按默认的fail
index = False) # 不将索引作为字段写入数据库

Matplotlib

Matplotlib是一个Python的2D绘图库。它以各种硬拷贝格式跨平台的交互式环境生成出版质量级别的图形。我们通常使用该库将数据可视化,更形象直观地暴露问题所在。


Scikit-learn

Scikit-learn(Sklearn)是机器学习中常用的第三方模块对常用的机器学习方法进行了封装,是建立在Numpy、Scipy、Matplotlib之上,简单高效的数据挖掘数据分析工具


Jupyter

Jupyter是一个Web应用程序,可以用来编写Python代码图表展示数值处理和转换数值模拟统计建模等各种任务。

Jupyter Notebook

快捷键 功能
Tab 自动补全
Shift + Tab 显示函数示例
Shift + Enter 执行当前,并跳转至下一Cell
Ctrl + Enter 执行当前,并留在当前Cell

Anaconda

Anaconda是一个免费开源的Python和R语言的发行版本,用于计算科学(数据科学、机器学习、大数据处理和预测分析),Anaconda致力于简化包管理和部署。Anaconda的包使用软件包管理系统Conda进行管理。

Anaconda3默认包含Python 3.7,但是用户可以创建虚拟环境来使用任意版本的Python包

Anaconda Navigator

Anaconda Navigator是包含在Anaconda中的图形用户界面,用户可以通过Anaconda Navigator启动应用,在不使用命令行的情况下管理软件包、创建虚拟环境和管理路径

Anaconda Navigator包含如下应用:

  • JupyterLab
  • Jupyter Notebook
  • QtConsole
  • Spyder
  • Glueviz
  • Orange
  • Rstudio
  • Visual Studio Code

问题

利用apply(),使用自定义函数更新DataFrame每行单元格的内容,但是得到的不是DataFrame对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# # 定性 用户18个月的消费
def active_status(data):
status = []
for i in range(18): # 一共18个月的记录
# 如果本月没有消费
if data[i] == 0:
if len(status) > 0: # 记录过状态
if status[i-1] == 'unreg': # 上个月是未注册
status.append('unreg') # 仍然是未注册
else: # 之前已注册(有过消费)
status.append('unactive') # 为不活跃
else: # 没记录过状态
status.append('unreg') # 为未注册

# 本月有消费
else:
if len(status) == 0: # 没记录过状态
status.append('new') # 新用户(第一次消费)
else: # 之前记录过
if status[i-1] == 'unactive': # 上个月为不活跃
status.append('return') # 更新为 回流
elif status[i-1] == 'unreg': # 上个月为未注册
status.append('new') # 新用户(第一次消费)
else:
status.append('active') # 更新为活跃
return status

cdnow_purchase.apply(active_status, axis=1) # 对每一行进行操作
  • 原因:

    自定义函数的返回值列表,而不是Series,导致apply()之后的结果不是一个DataFrame对象。

  • 解决方法:

    将自定义函数的返回值转换为Series对象

    1
    2
    --snip--
    return pd.Series(status, index=cdnow_purchase.columns) # index参数是原DataFrame对象的各列列名