数据分析——Pandas(进阶)

编辑器:jupyter notebook

一、数据统计

import numpy as np
import pandas as pd

1.  创建DataFrame对象

  • 生成100行3列[60,100)的随机整数
data = np.random.randint(60, 100, [100, 3])
index = range(1, 101)
columns = ['语文', '数学', '英语']
df = pd.DataFrame(
    data=data,
    index=index,
    columns=columns)
df

 

 2.  计算总成绩

  • 增加一列数据
df['总成绩'] = df.sum(axis=1)
df.head()

 

 3.  计算各科均值

  • 增加一行数据
df.loc['均值'] = df.mean(axis=0)
df

 

 4.  求每科的最大值

df.max()
语文      99.0
数学      99.0
英语      99.0
总成绩    564.0
dtype: float64

5.  求每科的最小值

df.min()
语文      60.0
数学      61.0
英语      60.0
总成绩    414.0
dtype: float64

6.  求每科的中位数

df.median()
语文      82.0
数学      81.0
英语      80.0
总成绩    484.0
dtype: float64

7.  求每科的众数

df.mode()

df['总成绩'].mode()    # 获取总成绩的众数
0    492.0
dtype: float64
# 如果存在多个众数,则求均值
df['总成绩'].mode().median()

492.0

8.  求数学的众数 

  • 如存在多个众数,则求它们的均值
df['数学'].mode()
0    90.0
dtype: float64

9.  方差和标准差

  • 生成2行10列[60,100)的随机整数
np.random.seed(666)          # 随机种子
data1 = np.random.randint(60,100,[2,10])
data2 = np.random.randint(60,120,[2,10])
data = np.vstack((data1,data2))    # 拼接data1,data2

index = ['张三','里斯','王五','赵六']
columns = [f"第{i}次测试" for i in range(1,11)]
data
array([[ 62,  90,  66,  69,  90,  96,  87,  90,  91,  88],
       [ 74,  93,  76,  99,  65,  78,  72,  75,  73,  65],
       [113,  80,  71,  84,  80,  96, 108, 108,  96,  90],
       [106, 118, 107,  76,  73,  96,  86,  65, 102,  99]])
df = pd.DataFrame(data=data,index=index,columns=columns)
df

10.  求方差、标准差和变异系数

df['方差'] = df.iloc[:,0:10].var(axis=1)
df['标准差'] = df.iloc[:,0:10].std(axis=1)
df['均值'] = df.iloc[:,0:10].mean(axis=1)
df['变异系数'] = df['标准差'] / df['均值']
df[['方差','标准差','均值','变异系数']].head()

 

 11.  分位数

  • 随机生成指定均值及标准差的数组
  • 均值为90,标准差为2000,100个元素
data = np.random.normal(90,2000,100)
columns = ['利润']
df = pd.DataFrame(data=data,columns=columns)
df

 

  • 计算25%的分位数,Q1,下四分位数
# .quantile(q=0.5, axis=0, numeric_only=True, interpolation='linear')
Q1 = df['利润'].quantile(0.25)
Q1

2808.5524129961677 

  • 计算75%的分位数,Q3,上四分位数
Q3 = df['利润'].quantile(0.75)
Q3
2808.5524129961677
  • 计算四分位距,Q3-Q1
IQR = Q3 - Q1
IQR
  • 异常值检测
  • 设定上限值和下限值
# 其中1.5可以根据需求修改
UPPER = min(Q3 + 1.5*IQR, df['利润'].max())   # 上限值,不超过最大值
LOWER = max(Q1 - 1.5*IQR, df['利润'].min())   # 下限值,不低于最小值
  • 异常值筛选
df[(df['利润'] > UPPER) | (df['利润'] < LOWER) ]
  • 异常值处理
  • 如高于上限值,则使用上限值替换
  • 如低于下限值,则使用下限值替换
UPPER,LOWER
# (2808.5524129961677, 2808.5524129961677)
df['利润'] = df['利润'].clip(LOWER,UPPER)
df

 二、数据格式化

import numpy as np
import pandas as pd

1.  设置小数位数

  • 生成5行5列[0, 1)的随机小数
df = pd.DataFrame(
    data=np.random.rand(5, 5),
    columns=['A', 'B', 'C', 'D', 'E'])
df

 

  • 所有列保留2位小数
# .round(decimals=0, *args, **kwargs)
df.round(2)

 

  • 使用字典方法
  • A列保留1位小数
  • B列保留2位小数
df.round({'A':1,'B':2})

 

  • 使用Series方法
  • C列保留1位小数
  • D列保留2位小数
s = pd.Series(index=['C','D'],data=[1,2])
df.round(s)

 

 2.  设置百分比

2.1  apply方法

  • Series和DataFrame对象均有此方法
  • 指定列保留0位小数的百分比
# .apply(func, axis=0, raw=False, result_type=None, args=(), **kwds)
df['A'].apply(lambda x: '{:.0%}'.format(x))
0    69%
1    46%
2    19%
3     5%
4    45%
Name: A, dtype: object
  • 指定列保留2位小数的百分比
# .apply(func, axis=0, raw=False, result_type=None, args=(), **kwds)
df['A'].apply(lambda x: '{:.2%}'.format(x))
0    68.96%
1    45.66%
2    18.66%
3     4.97%
4    44.94%
Name: A, dtype: object
  • 所有列保留2位小数的百分比
df.apply(lambda x: '{:.2%}'.format(x),axis=1)          # apply解决不了多列格式化,但是可以多列计算
TypeError: unsupported format string passed to Series.__format__
df.apply(lambda x: x*2,axis=1) 

# for循环 + apply  实现所有列保留2位小数的百分比
tb = df.copy()
for col in tb.columns:
    tb[col] = tb[col].apply(lambda x: '{:.2%}'.format(x))
tb

 2.2  applymap方法

  • DataFrame对象的方法,不支持单列的操作
  • 所有列保留2位小数的百分比
df.applymap(lambda x: '{:.2%}'.format(x))

 2.3  map方法

  • Series对象的方法,不支持多列的操作
  • 指定列保留2位小数的百分比
df['A'].map(lambda x: '{:.2%}'.format(x))
0    68.96%
1    45.66%
2    18.66%
3     4.97%
4    44.94%
Name: A, dtype: object
  • map字典操作,字段值映射
td = pd.DataFrame(data=[[1,187],
                        [0,170]],
                 columns=['性别','身高'])
td

 

dct = {1:'男',0:'女'}
td['性别'] = td['性别'].map(dct)
td

 3.  设置千位分隔符

  • 生成10行5列[1000, 10000)的随机整数
df = pd.DataFrame(
    data=np.random.randint(1000, 10000, [10, 5]),
    columns=['A', 'B', 'C', 'D', 'E'])
df

 

#指定列生成千位分隔符
df['A'].apply(lambda x: '{:,}'.format(x))    # 转换后类型为字符串
0    6,202
1    7,905
2    6,277
3    1,032
4    6,132
5    6,689
6    4,470
7    8,991
8    8,596
9    6,119
Name: A, dtype: object
# 所有列设置千位分隔符
df.applymap(lambda x: '{:,}'.format(x))

三、数据分组聚合

import pandas as pd
df = pd.read_excel('./data/demo_03.xlsx', sheet_name='Sheet1')
df

 1.  groupby分组函数

  • groupby函数 + 聚合函数

1.1  一列分组一列聚合

df.groupby('一级分类')['数量'].sum().to_frame()

df.groupby('一级分类')['数量'].sum().reset_index()

 1.2  一列分组多列聚合

df.groupby('一级分类')[['数量','金额']].sum()

 1.3  多列分组多列聚合

df.groupby(['一级分类','二级分类'])[['数量','金额']].sum()

df.groupby(['一级分类','二级分类'])[['数量','金额']].sum().reset_index()

 2.  对分组数据进行遍历

df.groupby('一级分类')['数量'].sum().to_frame()

groups = df[['订单编号','一级分类']].groupby('一级分类')
for key,data in groups:
    print(key)
    print(data)
    print("-"*100)
办公
                 订单编号 一级分类
0     US-2021-1357144   办公
1     CN-2021-1973789   办公
2     CN-2021-1973789   办公
3     US-2021-3017568   办公
4     CN-2020-2975416   办公
...               ...  ...
9948  CN-2018-1902956   办公
9949  CN-2018-1902956   办公
9951  CN-2019-3665640   办公
9955  CN-2021-4318875   办公
9958  CN-2018-3557528   办公

[5687 rows x 2 columns]
----------------------------------------------------------------------------------------------------
家具
                 订单编号 一级分类
7     CN-2019-4497736   家具
24    US-2020-4150614   家具
25    US-2020-4150614   家具
30    US-2020-4150614   家具
33    CN-2021-3230180   家具
...               ...  ...
9931  CN-2020-3557528   家具
9942  CN-2019-3438839   家具
9950  CN-2018-1902956   家具
9953  CN-2021-5146329   家具
9956  CN-2021-4318875   家具

[2244 rows x 2 columns]
----------------------------------------------------------------------------------------------------
技术
                 订单编号 一级分类
5     CN-2019-4497736   技术
10    CN-2018-4195213   技术
11    CN-2021-5801711   技术
13    CN-2019-2752724   技术
14    CN-2019-2752724   技术
...               ...  ...
9935  CN-2019-1364986   技术
9939  US-2021-4180783   技术
9952  US-2018-5128542   技术
9954  CN-2021-4318875   技术
9957  CN-2021-4318875   技术

[2028 rows x 2 columns]
----------------------------------------------------------------------------------------------------

3.  groupby+agg函数

3.1  指定列使用多个聚合函数

df.groupby('一级分类')[['数量','金额']].agg(['mean','sum'])

 3.2  指定列使用指定聚合函数

df.groupby('一级分类').agg({'数量':'mean','金额':'sum'})

df.groupby('一级分类').agg({'数量':['mean','sum'],'金额':['sum','max']})

df.groupby('一级分类').agg({'数量':'mean','金额':'max'}).rename(columns={'数量':'数量均值','金额':'金额最大值'})

df.groupby('一级分类').agg(数量均值=('数量','mean'),金额最大值=('金额','max'))

df.groupby('一级分类')['金额'].agg([('销售总额','sum'),('平均金额','mean')])

4.  自定义函数分组统计

  • 统计频数最高(行计数最多)的二级分类
  • 以及总数量和总金额
df['二级分类'].value_counts().index[0]
'装订机'
df.agg({'数量':np.sum,'金额':'sum','二级分类':lambda x: x.value_counts().index[0]}).reset_index()  # np ——> numpy的别名,需要引入numpy

  • 以一级分类为分组,统计销售数量最多的二级分类
# argmax:原始索引
# idxmax:自定义索引
df[df['一级分类'] == '办公'].groupby('二级分类')['数量'].sum().idxmax()
'装订机'
# 一级分类:办公/技术/家具
grouped = df.groupby('一级分类')
grouped.apply(lambda x: x.groupby('二级分类')['数量'].sum().idxmax())
一级分类
办公    装订机
家具     椅子
技术     电话
dtype: object

5.  groupby+transform函数

  • pd.DataFrame.transform()
  • 统计各省的金额以及占所属大区的比例
tb = df.groupby(['大区','省份']).agg({'金额':'sum'}).reset_index()
tb.head()

 

tb['大区金额'] = tb.groupby('大区')['金额'].transform('sum')
tb.head()

tb['大区金额占比'] = tb['金额'] / tb['大区金额']
tb['大区金额占比'] = tb['大区金额占比'].apply(lambda x: '{:.2%}'.format(x))   # 将数据转换为百分比格式
tb.head()

6.  多列合并计算

6.1  groupby+字典+聚合函数

df = pd.read_excel('./data/demo_03.xlsx', sheet_name='Sheet2')
df.head()

# 合并北上广深
dct = {'北京销量':'北上广深',
       '上海销量':'北上广深',
       '广州销量':'北上广深',
       '深圳销量':'北上广深',
       '长沙销量':'长沙'}
# 先设置行索引
df = df.set_index('二级分类')
df

df.groupby(dct,axis=1).sum()

6.2  groupby+Series+聚合函数

df = pd.read_excel('./data/demo_03.xlsx', sheet_name='Sheet2')

series = pd.Series(data=['北上广深','北上广深','北上广深','北上广深','长沙'],
          index=['北京销量','上海销量','广州销量','深圳销量','长沙销量'])

df = df.set_index('二级分类')
df.groupby(series,axis=1).sum()

 

 7.  pivot_table透视表

df = pd.read_excel('./data/demo_03.xlsx', sheet_name='Sheet1')

# pd.pivot_table(
#     data,  ——> df对象
#     values=None,    ——>Excel数据透视表的值
#     index=None,   ——> 数据透视表的行
#     columns=None,  ——>  数据透视表的列
#     aggfunc='mean',  ——>  数据透视表的计算类型
#     fill_value=None,  ——>  填充空值
#     margins=False,  ——>  是否显示合计
#     dropna=True,   ——>  是否删除缺失值(如整行缺失)
#     margins_name='All',  ——>  合计的别名
#     observed=False,
# ) -> 'DataFrame'
tb = pd.pivot_table(
    data = df,
    index='大区',
    columns='一级分类',
    values='金额',
    aggfunc='sum',
    margins=True,
    margins_name='合计')
tb = tb.reset_index()
# 重命名列索引的名称为None
tb.columns.name = None
tb

tb = pd.pivot_table(
    data = df,
    index=['大区','省份'],
    columns='一级分类',
    values='金额',
    aggfunc='sum',
    margins=True,
    margins_name='合计')
tb

 8.  crosstab交叉表

df = pd.read_excel('./data/demo_03.xlsx', sheet_name='Sheet1')

# pd.crosstab(
#     index,  ——> 行索引
#     columns,  ——> 列索引
#     values=None,  ——> 聚合字段
#     rownames=None,  ——> 行索引别名
#     colnames=None,  ——> 列索引别名
#     aggfunc=None,  ——> 聚合函数
#     margins=False,  ——> 是否显示合计
#     margins_name: str = 'All',  ——> 合计别名
#     dropna: bool = True,  ——> 是否删除缺失行(如整行缺失)
#     normalize=False,  ——> 值显示方式(True/all:总计百分比,index:行汇总百分比,columns:列汇总百分比)
# ) -> 'DataFrame'
pd.crosstab(
    index=df['大区'],
    columns=df['一级分类'],
    values=df['数量'],
    aggfunc='sum',
    margins=True,
    margins_name='合计',
    normalize=True
)

pd.crosstab(
    index=df['大区'],
    columns=df['一级分类'],
    values=df['数量'],
    aggfunc='sum',
    margins=True,
    margins_name='合计',
    normalize='index'    # 列汇总百分比
)

 9.  category数据类型

  • 主要为了提升运算性能
df = pd.read_csv('./data/demo_03_cat.csv')

df.dtypes
大区    object
金额     int64
dtype: object
  • 测试非category数据类型的运算时间
%%time
df.groupby('大区')['金额'].sum()
Wall time: 138 ms

Out[34]:

大区
东北    96128025
中南    96518713
华东    96023787
华北    96369953
西北    96451240
西南    95335807
Name: 金额, dtype: int64
  • 测试category数据类型的运算时间
df['大区'] = df['大区'].astype('category')

%%time
df.groupby('大区')['金额'].sum()
Wall time: 32 ms

Out[36]:

大区
东北    96128025
中南    96518713
华东    96023787
华北    96369953
西北    96451240
西南    95335807
Name: 金额, dtype: int64

四、数据偏移与切分

import numpy as np
import pandas as pd

df = pd.read_excel('./data/demo_04.xlsx', sheet_name='Sheet1')

df.head()

 1.  数据偏移

1.1  shift函数

  • 销量增长率 = (本期销量 - 上期销量) / 上期销量
# 日期不连续,必须设置时间序列为行索引
df = df.set_index('日期')

# df.shift(
#     periods=1,
#     freq=None,
#     axis=0,
#     fill_value=<object object at 0x000001B071412510>,
# ) -> 'DataFrame'
df['上期销量'] = df['销量'].shift(periods=1, freq='D')
df['销量增长率'] = (df['销量'] - df['上期销量']) / df['上期销量']
df.head()

 

 1.2  diff函数

  • 偏移后计算差异
# 不支持freq时间频率参数
# df.diff(periods: 'int' = 1, axis: 'Axis' = 0) -> 'DataFrame'
df['diff'] = df['销量'].diff(periods=1)
df.head()

 

 1.3  pct_change函数

  • 偏移后计算差异百分比
# df.pct_change(
#     periods=1,
#     fill_method='pad',
#     limit=None,
#     freq=None,
#     **kwargs,
# ) -> 'FrameOrSeries'
df['pct_change'] = df['销量'].pct_change(periods=1,freq='D')
df.head()

 

 1.4  rolling函数

  • 滚动计算,计算近3天平均销量
# df.rolling(
#     window: 'Union[int, timedelta, BaseOffset, BaseIndexer]',  ——> 设置窗口范围
#     min_periods: 'Optional[int]' = None,  ——>  解决窗口范围不足导致返回值为空的统计问题
#     center: 'bool_t' = False,
#     win_type: 'Optional[str]' = None,
#     on: 'Optional[str]' = None,
#     axis: 'Axis' = 0,
#     closed: 'Optional[str]' = None,
# )
df['近三天平均销量'] = df['销量'].rolling(window=3).mean()
df.head()

 

df['近三天平均销量'] = df['销量'].rolling(window=3,min_periods=1).mean()
df.head()

2.  数据切分

tb = pd.read_excel('./data/demo_04.xlsx', sheet_name='Sheet2')
tb.head()

 2.1  cut函数

2.1.1  指定箱数,等距分箱

  • 10组
# pd.cut(
#     x,
#     bins,  ——> 组数
#     right: bool = True,
#     labels=None,
#     retbins: bool = False,
#     precision: int = 3,
#     include_lowest: bool = False,
#     duplicates: str = 'raise',
#     ordered: bool = True,
# )
tb['等距分箱'] = pd.cut(tb['成绩'],bins=10,right=False)    # right = False,左闭右开, 默认左开右闭
tb['等距分箱'].value_counts()
[36.4, 48.2)        17
[107.2, 119.118)    15
[1.0, 12.8)         11
[24.6, 36.4)        10
[71.8, 83.6)        10
[60.0, 71.8)         9
[95.4, 107.2)        9
[48.2, 60.0)         7
[12.8, 24.6)         6
[83.6, 95.4)         6
Name: 等距分箱, dtype: int64

2.1.2  指定区间分箱

  • [0, 60, 80, 100, 120]
tb['指定区间分箱'] = pd.cut(x=tb['成绩'],bins=[0, 60, 80, 100, 120],right=False)
tb['指定区间分箱'].value_counts()
[0, 60)       51
[100, 120)    22
[60, 80)      17
[80, 100)     10
Name: 指定区间分箱, dtype: int64
# labels  分箱的标签
tb['指定区间分箱'] = pd.cut(x=tb['成绩'],bins=[0, 60, 80, 100, 120],labels=['不及格','一般','良好','优秀'],right=False)
tb['指定区间分箱'].value_counts()
不及格    51
优秀     22
一般     17
良好     10
Name: 指定区间分箱, dtype: int64

2.2  qcut函数

2.2.1  指定分位数,等频分箱

  • 4组
# pd.qcut(
#     x,
#     q,
#     labels=None,
#     retbins: bool = False,
#     precision: int = 3,
#     duplicates: str = 'raise',
# )
tb['等频分组'] = pd.qcut(x=tb['成绩'],q=4)   # 默认左开右闭
tb['等频分组'].value_counts()
(0.999, 34.75]    25
(34.75, 55.5]     25
(55.5, 94.25]     25
(94.25, 119.0]    25
Name: 等频分组, dtype: int64

2.2.2  指定分位数区间分箱

  • [0, 0.3, 0.5, 0.75, 1]
tb['指定分位数区间分箱'] = pd.qcut(x=tb['成绩'],q=[0, 0.3, 0.5, 0.75, 1])
tb['指定分位数区间分箱'].value_counts()
(0.999, 38.7]     30
(55.5, 94.25]     25
(94.25, 119.0]    25
(38.7, 55.5]      20
Name: 指定分位数区间分箱, dtype: int64
tb['成绩'].quantile(0.3)        # 38.7
tb['成绩'].median()             # 55.5
tb['成绩'].quantile(0.75)       # 94.25
tb['成绩'].max()                # 119

五、数据结构转换

import pandas as pd

1.  一列转为多列

  • split方法,已确定拆分结果的列数
  • 只将省份和城市拆分为新字段
df = pd.read_excel('./data/demo_05.xlsx', sheet_name='Sheet1')
df

 

tb = df['收货地址'].str.split(' ',expand=True)
df['省份'] = tb[0]
df['城市'] = tb[1]
df

df['收货地址'].str.split(' ',expand=False)
0    [广东省, 深圳市, 光明区, 公明街道]
1    [广东省, 深圳市, 光明区, 公明街道]
2    [广东省, 深圳市, 光明区, 公明街道]
3    [广东省, 深圳市, 宝安区, 沙井街道]
4    [广东省, 深圳市, 宝安区, 石岩街道]
Name: 收货地址, dtype: object
  • 将拆分结果全部转为新字段
df[['省','市','区','街道']] = df['收货地址'].str.split(' ',expand=True)
df

 2.  一列转为多列

  • split和join方法,不确定拆分结果的列数
df = pd.read_excel('./data/demo_05.xlsx', sheet_name='Sheet2')
df

# 先拆分商品信息,再关联至原表
df_split = df['商品信息'].str.split(',',expand=True)
df_split

 

# df.join(other, on=None, how='left', lsuffix='', rsuffix='', sort=False) -> 'DataFrame'
# 如果未指定关联键,则通过索引字段关联
df = df.join(df_split)
df

 3.  对元组/列表形式的字段进行拆分

df = pd.DataFrame({
    '订单编号': [1, 2, 3, 4],
    '数量和单价': [(1, 2),
              (3, 4),
              (5, 6),
              (7, 8)]})
df

df[['数量','单价']] = df['数量和单价'].apply(pd.Series)
df

 4.  列转为行

4.1  stack方法

  • 将除索引列外的字段转为最内层的行索引
df = pd.read_excel('./data/demo_05.xlsx', sheet_name='Sheet3')
df

 

# 将除索引列外的字段转为最内层的行索引
tb = df.set_index('门店')
tb

tb = tb.stack().reset_index()
tb.columns = ['门店','品类','销量']
tb

 4.2  melt方法

  • 宽表转为长表
df = pd.read_excel('./data/demo_05.xlsx', sheet_name='Sheet3')
df

 

# pd.melt(
#     frame: 'DataFrame',
#     id_vars=None,   ——————> 位置保持不变的字段
#     value_vars=None,  ——————>   需要转换的字段
#     var_name=None,   ——————>   variable的别名 
#     value_name='value',   ——————>   value的别名
#     col_level=None,
#     ignore_index: bool = True,
# ) -> 'DataFrame'
pd.melt(frame=df,id_vars='门店',value_vars=['手机','电脑'],var_name='品类',value_name='销量')

5.  行转为列

5.1  unstack方法

  • 将最内层的行索引转为字段
df = pd.read_excel('./data/demo_05.xlsx', sheet_name='Sheet4')
df

 

tb = df.set_index(['门店','品类'])
tb

tb = tb.unstack()
tb.columns = tb.columns.droplevel(0)
tb.columns
Index(['手机', '电脑'], dtype='object', name='品类')
tb.columns.name = None
tb.reset_index()

 

 5.2  pivot方法

  • 透视列,没有聚合功能
df = pd.read_excel('./data/demo_05.xlsx', sheet_name='Sheet4')
df

 

# pivot   没有聚合
# pivot_table   可以聚合
# .pivot(index=None, columns=None, values=None) -> 'DataFrame'
tb = df.pivot(index='门店',columns='品类',values='销量')
tb

tb.columns.name = None
tb.reset_index()

6.  DataFrame转为字典

df = pd.read_excel('./data/demo_05.xlsx', sheet_name='Sheet5')
df.head()

tb = df.set_index('用户ID')
tb.to_dict()

'''
{'用户姓名': {'uid00160': '刘备',
  'uid00161': '关羽',
  'uid00162': '张飞',
  'uid00182': '赵云',
  'uid00188': '马超'}}
'''

tb.to_dict()['用户姓名']
'''
{'uid00160': '刘备',
 'uid00161': '关羽',
 'uid00162': '张飞',
 'uid00182': '赵云',
 'uid00188': '马超'}
'''

7.  DataFrame转为列表

df = pd.read_excel('./data/demo_05.xlsx', sheet_name='Sheet5')
df.head()

df.values.tolist()

'''
[['uid00160', '刘备'],
 ['uid00161', '关羽'],
 ['uid00162', '张飞'],
 ['uid00182', '赵云'],
 ['uid00188', '马超']]
'''

8.  DataFrame转为元组

df = pd.read_excel('./data/demo_05.xlsx', sheet_name='Sheet5')
df

tuple(tuple(x) for x in df.values)
'''
(('uid00160', '刘备'),
 ('uid00161', '关羽'),
 ('uid00162', '张飞'),
 ('uid00182', '赵云'),
 ('uid00188', '马超'))
'''

9.  DataFrame转为HTML

df = pd.read_excel('./data/demo_05.xlsx', sheet_name='Sheet5')
df

df.to_html('网页.html')

 六、数据合并

import pandas as pd

1.  merge横向合并

df1 = pd.read_excel('./data/demo_06.xlsx', sheet_name='基本信息')
df2 = pd.read_excel('./data/demo_06.xlsx', sheet_name='联系方式')

df1

df2

1.1  左连接

# pd.merge(
#     left,  ——————>  左表
#     right,  ——————>  右表
#     how: str = 'inner',  ——————>  连接方式,默认内链接
#     on=None,  ——————>  关联字段
#     left_on=None,  ——————>  左表的关联字段
#     right_on=None,  ——————>  右表的关联字段
#     left_index: bool = False,
#     right_index: bool = False,
#     sort: bool = False,  ——————>  排序,默认降序
#     suffixes=('_x', '_y'),
#     copy: bool = True,
#     indicator: bool = False,  ——————>  指示器,值为表中的数据是否两个表中都存在或只存在于一个表中
#     validate=None,
# ) -> 'DataFrame'
pd.merge(df1,df2,how='left',left_on='工号',right_on='编号',indicator=True)

 1.2  右连接

pd.merge(df1,df2,how='right',left_on='工号',right_on='编号',indicator=True)

1.3  外连接

pd.merge(df1,df2,how='outer',left_on='工号',right_on='编号',indicator=True)

 1.4  内连接

pd.merge(df1,df2,how='inner',left_on='工号',right_on='编号',indicator=True)

 1.5  交叉连接

  • 笛卡尔积
pd.merge(df1,df2,how='cross')       # 类似于两表相成

 

2.  join横向合并

df1 = pd.read_excel('./data/demo_06.xlsx', sheet_name='基本信息')
df2 = pd.read_excel('./data/demo_06.xlsx', sheet_name='联系方式')

df1

df2

# 先将将两表的关联字段设置为行索引
df1.set_index('工号',inplace=True)
df2.set_index('编号',inplace=True)

2.1  左连接

df1.join(df2,how='left')

 2.2  右连接

df1.join(df2,how='right')

 2.3  外连接

df1.join(df2,how='outer')

 2.4  内连接

df1.join(df2,how='inner')

 3.  concat横向合并

df1 = pd.read_excel('./data/demo_06.xlsx', sheet_name='基本信息')
df2 = pd.read_excel('./data/demo_06.xlsx', sheet_name='联系方式')


df1

df2

# 先将将两表的关联字段设置为行索引
df1.set_index('工号',inplace=True)
df2.set_index('编号',inplace=True)

3.1  外连接

# pd.concat(
#     objs: Union[Iterable[ForwardRef('NDFrame')], Mapping[Union[Hashable, NoneType], ForwardRef('NDFrame')]],
#     axis=0,
#     join='outer',
#     ignore_index: bool = False,
#     keys=None,
#     levels=None,
#     names=None,
#     verify_integrity: bool = False,
#     sort: bool = False,
#     copy: bool = True,
# ) -> Union[ForwardRef('DataFrame'), ForwardRef('Series')]
pd.concat(objs=[df1,df2],axis=1,join='outer')

 3.2  内连接

pd.concat(objs=[df1,df2],axis=1,join='inner')

 4.  concat纵向合并

df3 = pd.read_excel('./data/demo_06.xlsx', sheet_name='1月订单')
df4 = pd.read_excel('./data/demo_06.xlsx', sheet_name='2月订单')

df3

df4

pd.concat(objs=[df3,df4],axis=0,ignore_index=True)    # ignore_index=True 保持索引号的连续性

# 使用append方法实现
df3.append(df4,ignore_index=True)       # ignore_index=True 保持索引号的连续性

5.总结

表的横向合并:merge方法(支持交叉连接),pd.merge 或 df.merge;join方法(不支持交叉连接),连式写法 df.join;

表的纵向合并:concat方法(横向合并仅支持内外连接)或 append方法

七、数据导出

import pandas as pd
# pip install sqlalchemy
from sqlalchemy import create_engine
import pymysql

df = pd.read_excel('./data/demo_07.xlsx')

df.head()

 1.  导出Excel

df.to_excel('导出Excel.xlsx',sheet_name='订单明细1',index=False)     # index = False 不导入索引

2.  导出CSV

df.to_csv('导出CSV.csv',index=False)

3.  导出TxT

df.to_csv('导出Txt.txt',sep='\t',index=False)     # sep 设置分隔符

4.  导出Json

  • orient: {'split', 'records', 'index', 'columns', 'values', 'table'}
df.to_json('导出Json.json',orient='split',force_ascii=False)      # orient 设置结构类型;force_ascii=False  取消ascii编码格式以显示汉字

5.  导出数据库

engine = create_engine('mysql+pymysql://用户名:密码@localhost:3306/库名?charset=utf8')
# df.to_sql(
#     name: 'str',  ——————>    表名
#     con,  ——————>   连接引擎
#     schema=None,  ——————>   库名
#     if_exists: 'str' = 'fail',  ——————>  replace: 如果表存在则新建,存在则覆盖   ;append:如果表存在则新建,存在则追加
#     index: 'bool_t' = True,   ——————>   False:不含索引
#     index_label=None,
#     chunksize=None,
#     dtype=None,
#     method=None,
# ) -> 'None'
df.to_sql(schema='库名',name='表名',con=engine,index=False,if_exists='append')

6.  导出Excel

  • 同一工作簿下的不同工作表
df1 = pd.read_excel('./data/demo_07.xlsx', sheet_name='1月订单')
df2 = pd.read_excel('./data/demo_07.xlsx', sheet_name='2月订单')

df1.head()

df2.head()

# 创建数据写入器
write = pd.ExcelWriter('导出Excel不同表.xlsx')
# 写入
df1.to_excel(write,sheet_name='1月订单')
df2.to_excel(write,sheet_name='2月订单')
# 保存
write.save()
# 关闭
write.close()

八、日期处理与采样

import pandas as pd

1.  解析日期

df = pd.read_excel('./data/demo_08.xlsx', sheet_name='Sheet1')

df

# pd.to_datetime(
#     arg: Union[~DatetimeScalar, List, Tuple, ~ArrayLike, ForwardRef('Series')],
#     errors: str = 'raise', ——————————>    ignore:当无法解析时直接忽略;raise:无法解析时抛出异常;coerce:无法解析返回空值,强制转换
#     dayfirst: bool = False,
#     yearfirst: bool = False,
#     utc: Union[bool, NoneType] = None,
#     format: Union[str, NoneType] = None,
#     exact: bool = True,
#     unit: Union[str, NoneType] = None,
#     infer_datetime_format: bool = False,
#     origin='unix',
#     cache: bool = True,
# ) -> Union[pandas.core.indexes.datetimes.DatetimeIndex, ForwardRef('Series'), ~DatetimeScalar, ForwardRef('NaTType')]
df['订单日期'] = pd.to_datetime(df['订单日期'])
df

 2.  组合日期

df = pd.read_excel('./data/demo_08.xlsx', sheet_name='Sheet2')

df

df['日期'] = pd.to_datetime(df)
df

3.  提取日期信息

df = pd.read_excel('./data/demo_08.xlsx', sheet_name='Sheet3')

df

df['订单日期'] = pd.to_datetime(df['订单日期'])
df

df['年'] = df['订单日期'].dt.year
df['月'] = df['订单日期'].dt.month
df['季度'] = df['订单日期'].dt.quarter
df['日'] = df['订单日期'].dt.day
df['周几'] = df['订单日期'].dt.day_name()
df['是否年底'] = df['订单日期'].dt.is_year_end
df

df.groupby(['年','月']).size()
年     月 
2020  1     1
      2     1
      3     1
      6     1
      9     1
      12    1
dtype: int64

4.  重采样

  • 针对日期字段分组聚合的快捷方式
  • 重采样:升采样,降采样
  • 升采样:低频率到高频率采样
  • 降采样:高频率到低频率采样
  • Time series / date functionality — pandas 1.5.0 documentation
  • 时间频率:
  • AS: 年初
  • A: 年末
  • YS: 年初
  • Y: 年末
  • QS: 季初
  • Q: 季末
  • MS: 月初
  • M: 月末
  • W: 周
  • D: 日
  • H: 时
  • T: 分
  • min: 分
  • S: 秒
  • W-SUN: 周(周日结束)Sunday
  • W-MON: 周(周一结束)Monday
  • W-TUE: 周(周二结束)Tuesday
  • W-WED: 周(周三结束)Wednesday
  • W-THU: 周(周四结束)Thursday
  • W-FRI: 周(周五结束)Friday
  • W-SAT: 周(周六结束)Saturday
df = pd.read_excel('./data/demo_08.xlsx', sheet_name='Sheet4')
df.head()

 

4.1  设置日期为索引

tb = df.set_index('订单日期')

4.2  按月统计数据

# tb.resample(
#     rule,
#     axis=0,
#     closed: 'Optional[str]' = None,
#     label: 'Optional[str]' = None,
#     convention: 'str' = 'start',
#     kind: 'Optional[str]' = None,
#     loffset=None,
#     base: 'Optional[int]' = None,
#     on=None,
#     level=None,
#     origin: 'Union[str, TimestampConvertibleTypes]' = 'start_day',
#     offset: 'Optional[TimedeltaConvertibleTypes]' = None,
# ) -> 'Resampler'
tb.resample('M')['销售额'].sum().to_period('M').to_frame()

tb.resample('M').agg({'数量':'sum','销售额':'mean'}).to_period('M')

4.3  按季统计数据

tb.resample('Q').agg({'数量':'sum','销售额':'mean'}).to_period('Q')

4.4  按年统计数据

tb.resample('Y').agg({'数量':'sum','销售额':'mean'}).to_period('Y')

4.5  日期切片操作

# 必须先设置日期为索引,排序后再进行切片处理
tb = tb.sort_index()

tb['2020-01-01':'2020-01-15']

5.  OHLC函数

  • open(开始值,开盘价)
  • high(最高值)
  • low(最低值)
  • close(结束值,收盘价)
# pandas: 早起主要做金融数据分析
tb.resample('M')['销售额'].ohlc().to_period('M')

 

 6.  sample函数

  • 随机抽样
tb = pd.DataFrame(
    data=[['貂蝉', 4],
          ['西施', 3],
          ['王昭君', 2],
          ['杨玉环', 1]],
    columns=['贵妃', '权重'])

tb

 

 6.1  随机抽取1个

# tb.sample(
#     n=None,  ————————>    指定数量抽样
#     frac=None,  ————————>    指定占比抽样
#     replace=False,  ————————>    是否允许重复抽样
#     weights=None,  ————————>    按照权重抽样,权重数值越大越容易抽中
#     random_state=None,  ————————>    设置随机种子
#     axis=None,
# ) -> 'FrameOrSeries'
tb.sample(n=1)

 6.2  随机抽取50%

tb.sample(frac=0.5)

 6.3  允许重复抽样

tb.sample(n=2,replace=True)

 6.4  按权重抽样

df1 = pd.DataFrame()
for i in range(1000):
    df2 = tb.sample(n=1,weights='权重')
    df1 = df1.append(df2)
    
df1.value_counts()
贵妃   权重
貂蝉   4     395
西施   3     293
王昭君  2     200
杨玉环  1     112
dtype: int64

6.5  设置随机种子

tb.sample(n=1,random_state=3)

九、时间序列

import pandas as pd
import arrow
# github时间模块标星最高的第三方库
# pip install arrow

 1.  arrow时间模块

  • 获取当前时间
arrow.now()
<Arrow [2022-09-27T15:48:40.889286+08:00]>
  • 从字符串解析日期
arrow.get('2021/02/14 12:12:21','YYYY/MM/DD HH:mm:SS')
<Arrow [2021-02-14T12:12:00.210000+00:00]>
  • 实例化日期
arrow.get(2021,2,14)
<Arrow [2021-02-14T00:00:00+00:00]>
  • 获取当前时间
  • 获取年份部分
  • 获取日期部分
  • 获取时间部分
  • 向后偏移3周
  • 明年的上月的今天
  • 向前偏移1小时
  • 向后偏移2小时
now = arrow.now()
now.year,  now.date(),  now.time(),  now.shift(weeks=3),  now.shift(years=1,months=-1),  now.shift(hours=-1),  now.shift(hours=2)
(2022,
 datetime.date(2022, 9, 27),
 datetime.time(15, 55, 43, 706035),
 <Arrow [2022-10-18T15:55:43.706035+08:00]>,
 <Arrow [2023-08-27T15:55:43.706035+08:00]>,
 <Arrow [2022-09-27T14:55:43.706035+08:00]>,
 <Arrow [2022-09-27T17:55:43.706035+08:00]>)

2.  生成时间序列

  • 开始:2021-01-01,结束:2021-01-31,间隔5天
# pd.date_range(
#     start=None,
#     end=None,
#     periods=None,
#     freq=None,
#     tz=None,
#     normalize=False,
#     name=None,
#     closed=None,
#     **kwargs,
# ) -> pandas.core.indexes.datetimes.DatetimeIndex
pd.date_range(start='2021-01-01',end='2021-01-31',freq='5D')
DatetimeIndex(['2021-01-01', '2021-01-06', '2021-01-11', '2021-01-16',
               '2021-01-21', '2021-01-26', '2021-01-31'],
              dtype='datetime64[ns]', freq='5D')
  • 开始:2021-01-01,间隔2天,数目5个
pd.date_range(start='2021-01-01',freq='2D',periods=5)
DatetimeIndex(['2021-01-01', '2021-01-03', '2021-01-05', '2021-01-07',
               '2021-01-09'],
              dtype='datetime64[ns]', freq='2D')
  • 开始:2021-01-01,结束:2021-01-31,间隔1周
pd.date_range(start='2021-01-01',end='2021-01-31',freq='W')
DatetimeIndex(['2021-01-03', '2021-01-10', '2021-01-17', '2021-01-24',
               '2021-01-31'],
              dtype='datetime64[ns]', freq='W-SUN')
  • 开始:2021-01-01,间隔3小时,数目8个
pd.date_range(start='2021-01-01',freq='3H',periods=8)
DatetimeIndex(['2021-01-01 00:00:00', '2021-01-01 03:00:00',
               '2021-01-01 06:00:00', '2021-01-01 09:00:00',
               '2021-01-01 12:00:00', '2021-01-01 15:00:00',
               '2021-01-01 18:00:00', '2021-01-01 21:00:00'],
              dtype='datetime64[ns]', freq='3H')
  • 开始:2021-01-01 09:00,间隔1分钟,9点开始,数目12个
pd.date_range(start='2021-01-01 09:00',freq='min',periods=12)
DatetimeIndex(['2021-01-01 09:00:00', '2021-01-01 09:01:00',
               '2021-01-01 09:02:00', '2021-01-01 09:03:00',
               '2021-01-01 09:04:00', '2021-01-01 09:05:00',
               '2021-01-01 09:06:00', '2021-01-01 09:07:00',
               '2021-01-01 09:08:00', '2021-01-01 09:09:00',
               '2021-01-01 09:10:00', '2021-01-01 09:11:00'],
              dtype='datetime64[ns]', freq='T')
  • 开始:2021-01-01,结束:2021-12-31,间隔1月,月末最后1天
pd.date_range(start='2021-01-01',end='2021-12-31',freq='M')
DatetimeIndex(['2021-01-31', '2021-02-28', '2021-03-31', '2021-04-30',
               '2021-05-31', '2021-06-30', '2021-07-31', '2021-08-31',
               '2021-09-30', '2021-10-31', '2021-11-30', '2021-12-31'],
              dtype='datetime64[ns]', freq='M')
  • 开始:2021-01-01,间隔1年,年末最后1天,数目6个
pd.date_range(start='2021-01-01',freq='Y',periods=6)
DatetimeIndex(['2021-12-31', '2022-12-31', '2023-12-31', '2024-12-31',
               '2025-12-31', '2026-12-31'],
              dtype='datetime64[ns]', freq='A-DEC')
  • 开始:2021-01-01,间隔1年,年初最后1天,数目6个

pd.date_range(start='2021-01-01',freq='YS',periods=6)
DatetimeIndex(['2021-01-01', '2022-01-01', '2023-01-01', '2024-01-01',
               '2025-01-01', '2026-01-01'],
              dtype='datetime64[ns]', freq='AS-JAN')

3.  时间序列加减

df = pd.DataFrame(
    data=pd.date_range('2021-01-01', freq='3D', periods=15),
    columns=['日期'])
df

 

3.1  时间序列加减,天为单位

df['日期加十天'] = df['日期'] + pd.DateOffset(days=10)
df['日期减十天'] = df['日期'] + pd.DateOffset(days=-10)

3.2  时间序列加减,月为单位

df['日期减两月'] = df['日期'] - pd.DateOffset(months=2)
df['日期加两月'] = df['日期'] + pd.DateOffset(months=2)

3.3  时间序列加减,周为单位

df['日期减两周'] = df['日期'] - pd.DateOffset(weeks=2)
df['日期加两周'] = df['日期'] + pd.DateOffset(weeks=2)

3.4  向前(未来)偏移

  • 得到最近的月末时间点
df['下期月末'] = df['日期'] + pd.offsets.MonthEnd(n=1)

3.5  向后(过去)偏移

  • 得到最近的季末时间点
df['下期季度末'] = df['日期'] - pd.offsets.QuarterEnd(n=1)
df

 3.6  实例化日期

pd.Timestamp('now')    # pandas 获取当前时间戳
# Timestamp('2022-09-27 16:11:10.717712')

pd.Timestamp(2022,1,1,12,45,30)   # 创建指定时间戳
# Timestamp('2022-01-01 12:45:30')

十、Excel多表合并

import pandas as pd
import os
# 合并demo_10文件夹下所有xlsx


path = 'data/demo_10/'
# 用于存储所有数据
df = pd.DataFrame()
for i in os.listdir(path):
    # print(i)
    # 完整文件路径
    name = os.path.join(path,i)
    # print(name)
    # 单个文件的数据
    data = pd.read_excel(name)
    # 将单个文件的数据逐个插入
    df = df.append(data)
df

 

十一、数据可视化

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# pyforest:懒人导包神器

1.  配置参数

# 在线作图
%matplotlib inline
# 解决中文无法显示
plt.rcParams['font.sans-serif'] = 'SimHei'
# 负号显示异常的问题
plt.rcParams['axes.unicode_minus'] = False
# 将图片格式设置为 svg 格式,提高图标质量
%config Inlinebackend.figure_format = 'svg'

2.  导入数据

df = pd.read_csv('./data/diamonds.csv')
df.head()

 3.  直方图

df.hist(column='克拉',figsize=(10,10),color='#fff999')
array([[<AxesSubplot:title={'center':'克拉'}>]], dtype=object)

 4.  普通箱线图

  • 克拉分布
df.boxplot(column='克拉',figsize=(10,6))

 

 5.  分组箱线图

  • 各纯净度的价格分布
df.boxplot(column='价格',by='纯净度',figsize=(10,6))
<AxesSubplot:title={'center':'价格'}, xlabel='纯净度'>

  • 各纯净度的克拉分布
df.boxplot(column='克拉',by='纯净度',figsize=(10,6))
<AxesSubplot:title={'center':'克拉'}, xlabel='纯净度'>

 6.  核密度估计图

# plot指的是核密度图,作用类似于直方图
df['克拉'].plot(kind='kde',figsize=(10,6),xlim=(0,4))

# <AxesSubplot:ylabel='Density'>

7.  普通柱形图

  • 单个维度,各纯净度的频数分布
tb = pd.crosstab(index=df['纯净度'],columns='频数')

tb.columns.name = None

tb.plot(kind='bar',figsize=(10,6))

# <AxesSubplot:xlabel='纯净度'>

 8.  簇状柱形图

  • 多个维度,各纯净度各颜色的频数分布
tb = pd.crosstab(index=df['纯净度'],columns=df['颜色'])
tb.plot(kind='bar',figsize=(10,6))
<AxesSubplot:xlabel='纯净度'>

9.  堆积柱形图

tb.plot(kind='bar',figsize=(10,6),stacked=True)

# <AxesSubplot:xlabel='纯净度'>

 10.  散点图

df.plot(kind='scatter',x='克拉',y='价格',figsize=(10,6))

# <AxesSubplot:xlabel='克拉', ylabel='价格'>

 11.  折线图

year = [str(y)+'年' for y in range(2001, 2021)]
sale = np.random.randint(100, 120, 20)
df = pd.DataFrame({'year': year, 'sale': sale})

df

df.plot(x='year',y='sale',figsize=(10,6),ylim=[0,130])

# <AxesSubplot:xlabel='year'>

 

 12.  保存本地

plot = df.plot(x='year',y='sale',figsize=(10,6),ylim=[0,130])

plot.get_figure().savefig('折线图.png')

  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值