介绍
R语言数据可视化的强大之处在于ggplot2,而plotnine就是python版的ggplot2,语法与R语言的ggplot2基本一致,无论是从语法简洁性、作图灵活性、美观度等方面,个人认为plotnine均可胜于python中那些常用的可视化模块(例如:matplotlib、seaborn等)。
目前国内在用plotnine的人似乎还不多,中文网站上很难搜索到比较全面的使用方法,作为数据分析工作者,出图的速度直接影响数据挖掘的效率,所以撰此文目的在于加强自己对可视化模块plotnine的学习巩固,同时也可以分享给正在学习可视化的同学。
初步计划持续更新五个系列图表绘制案例及N个真实的数据分析项目:
五个系列图表:趋势型图表、分布型图表、类别比较型图表、数据关系型图表、局部整体型图表
N个真实案例:分析案例均源自本人实际经手的数据项目,会持续更新,会做脱敏
最近更新时间:2022.05.22
快速通道↓↓↓
目录
准备
模块安装、导入
pip install plotnine
from plotnine import *
汉字乱码问题解决
import matplotlib as plt
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
一、趋势型图表系列
场景case:
某互联网公司为了提高裂变类活动的转化率,分别设计并同时上线了两种活动形式(A活动、B活动),通过对比AB两种活动的累计转化率,取较优者决策。
样本示例:
1.1折线图
def plot_a_1(df):
base_plot = (
ggplot(df, aes(x='日期', y='转化率', group='分组', color='分组'))
+geom_line(size=1)
+scale_x_date(name='日期', breaks='2 weeks') #解决x轴标签覆盖问题
+scale_fill_hue(s=0.90, l=0.65, h=0.0417, color_space='husl') #自动配色
#若自定义线条颜色可将上一行代码替换为:
#+scale_color_manual(values=('#084081', '#7bccc4'))
#若在折线图上添加值标签可用下行代码(此图不建议添加值标签,不美观)
#+geom_text(aes(x='日期',y='转化率',label='转化率'),color='black')
+xlab('时间') #重命名x轴名称
+ylab('CVR') #重命名y轴名称
+ggtitle('AB组活动转化率趋势图')
+theme_matplotlib()
)
print(base_plot)
base_plot.save('page/趋势图/图a_1.jpeg', dpi=1000)
1.2面积图
def plot_a_2(df):
base_plot = (
ggplot(df, aes(x='日期', y='转化率', group='分组'))
+geom_area(aes(fill='分组'), alpha=0.75, position='identity')
+geom_line(aes(color='分组'), size=0.75)
+scale_x_date(name='日期', breaks='2 weeks') #解决x轴日期标签间隔、覆盖问题
+scale_fill_hue(s=0.90, l=0.65, h=0.0417, color_space='husl')
+xlab('时间')
+ylab('CVR')
+ggtitle('AB组活动转化率趋势图')
+theme_matplotlib()
)
print(base_plot)
base_plot.save('page/趋势图/图a_2.jpeg', dpi=1000)
1.3夹层填充面积图
绘图前需要先对样本数据做一下处理,处理代码及处理后的dataframe如下:
def plot_a_3(df):
df_A = df[df['分组'] == 'A组']
df_B = df[df['分组'] == 'B组']
df_new = pd.merge(df_A, df_B, how='left', on='日期', suffixes=('A组', 'B组'))
df_new['最小值'] = df_new.apply(lambda x: x[['转化率A组', '转化率B组']].min(), axis=1)
df_new['最大值'] = df_new.apply(lambda x: x[['转化率A组', '转化率B组']].max(), axis=1)
#df_new['比较'] = df_new.apply(lambda x: 'A组高' if x['转化率A组'] - x['转化率B组'] > 0 else 'B组高', axis=1)
df_new['ymin1'] = df_new['最小值']
df_new.loc[(df_new['转化率A组']-df_new['转化率B组']) > 0, 'ymin1'] = np.nan
df_new['ymin2'] = df_new['最小值']
df_new.loc[(df_new['转化率A组']-df_new['转化率B组']) <= 0, 'ymin2'] = np.nan
df_new['ymax1'] = df_new['最大值']
df_new.loc[(df_new['转化率A组']-df_new['转化率B组']) > 0, 'ymax1'] = np.nan
df_new['ymax2'] = df_new['最大值']
df_new.loc[(df_new['转化率A组']-df_new['转化率B组']) <= 0, 'ymax2'] = np.nan
base_plot=(
ggplot()
+geom_ribbon(df_new, aes(x='日期', ymin='ymin1', ymax='ymax1', group=1), alpha=0.5, fill='#00B2F6', color='none')
+geom_ribbon(df_new, aes(x='日期', ymin='ymin2', ymax='ymax2', group=1), alpha=0.5, fill='#FF6B5E', color='none')
+geom_line(df, aes(x='日期', y='转化率', group='分组', color='分组'), size=1)
+scale_x_date(name='日期', breaks='2 weeks')
+theme_matplotlib()
)
print(base_plot)
做以上处理的目的是为作图做准备:
①最大值、最小值:用于绘制夹层的填充线;
②之所以ymin1/ymax1 与 ymin2/ymax2 区分开,是为了区分填充线颜色
③之所以绘制两次ribbon除了原因②之外,也是为了避免出现多余的填充
1.4百分比堆积面积图
场景case:
某互联网公司在全国城市推广了一种引流活动,并将城市划分了7个等级(一线、新一线、二线….其他),因为来自不同城市等级的用户质量不同,所以需要关注每个活动期次中用户来源的城市等级构成变化。
样本示例:
同一‘活动期次’中,‘同期占比’之和为1,虽然样本中有这一数据,但我们在绘图时不会用到”同期占比”,所以在实际绘图导入数据时无需计算该值。
def plot_a_4(df):
base_plot = (
ggplot(df, aes(x='活动期次', y='参与人数', fill='城市等级', group='城市等级'))
+geom_area(position='fill', alpha=1)
+geom_line(position='fill', size=0.25, color='black')
+scale_fill_hue(s=0.99, l=0.65, h=0.0417, color_space='husl')
+xlab('活动期次')
+ylab('同期占比')
+theme_matplotlib()
)
print(base_plot)
base_plot.save('page/趋势图/图a_4.jpeg', dpi=1000)
补充:与1.2面积图不同的是position参数的设置,‘identity’表示不改变位置,而‘fill’则表示在多数据系列时以百分比的形式堆叠(数量会自动转为占比后在y轴显示),另外比较常用的还有position=’stack’,意思是堆积,假如仅把上图position参数fill换成stack,那么输出如下:
图解:例如第”1期”的y轴值约2500,该值表示所有城市等级用户数量之和。
也就是说:每个系列的开始点是前一个数据系列的结束点,y轴的值表示多个系列的数值之和。
而假如,仅把上述代码中position参数换成identity,则输出如下:
此时y轴的值仅表示某一单个系列的数值。
二、类别比较型图表系列
2.1单数据系列柱形图
def plot_b_1():
df = pd.DataFrame({'Cut': ['fair', 'good', 'very good', 'premium', 'ideal'], 'Price': [4300, 3800, 3950, 4700, 3500]})
base_plot = (
ggplot(df, aes(x='Cut', y='Price'))
+geom_bar(stat='identity', width=0.8, colour='black', size=0.25, fill='#FF6B5E', alpha=1)
+theme_matplotlib()
)
print(base_plot)
base_plot.save('page/类别比较/图b_1_1.jpeg', dpi=1000)
补充:类别排序问题
绘制柱形图、条形图系列的最大潜在问题就是排序,
plotnine绘制柱形图时,X轴变量默认会按照输入的数据顺序绘制,若要调整顺序,需对dataframe的类别顺序进行调整(pandas.Categorical方法的使用),
假如要将上图的绘制顺序改为按”Price”的值降序排列,则代码为:
def plot_b_1():
df = pd.DataFrame({'Cut': ['fair', 'good', 'very good', 'premium', 'ideal'], 'Price': [4300, 3800, 3950, 4700, 3500]})
sort_df = df.sort_values(by='Price', ascending=False)
sort_df['Cut'] = pd.Categorical(sort_df['Cut'], categories=sort_df['Cut'], ordered=True)
base_plot = (
ggplot(sort_df, aes(x='Cut', y='Price'))
+geom_bar(stat='identity', width=0.8, colour='black', size=0.25, fill='#FF6B5E', alpha=1)
+theme_matplotlib()
)
print(base_plot)
base_plot.save('page/类别比较/图b_1_2.jpeg', dpi=1000)
若要自定义类别顺序,可以修改如下代码:
例如将顺序修改为 fair→premium→very good→ideal→good
df = pd.DataFrame({'Cut': ['fair', 'good', 'very good', 'premium', 'ideal'], 'Price': [4300, 3800, 3950, 4700, 3500]})
df['Cut'] = pd.Categorical(sort_df['Cut'], categories=['fair','premium','very good','ideal','good'], ordered=True)
2.2多数据系列柱形图
def plot_b_2():
df = pd.DataFrame({'Cut': ['fair', 'good', 'very good', 'premium', 'ideal'],
'Price_A': [4300, 3800, 3950, 4700, 3500],
'Price_B': [4000, 3200, 4300, 3700, 3000]})
#做数据处理,目的是以Price_A的值降序绘制
df = df.sort_values(by='Price_A', ascending=False)
#原始数据是二维表,需要将其转化成一维表
df_new = pd.melt(df, id_vars='Cut')
df_new['Cut'] = pd.Categorical(df_new['Cut'], categories=df['Cut'])
base_plot = (
ggplot(df_new, aes(x='Cut', y='value', fill='variable'))
+geom_bar(stat='identity', color='black', position='dodge', width=0.7, size=0.25)
#dodge水平抖动放置、stack垂直堆叠放置、identity不做调整(多系列情况下会存在覆盖问题)、fill百分比堆叠
+scale_fill_hue(s=0.90, l=0.65, h=0.0417, color_space='husl')
#若自定义填充色
#+scale_fill_manual(values=['#FF6B5E','#00B2F6'])
+theme_matplotlib()
)
print(base_plot)
base_plot.save('page/类别比较/图b_2.jpeg', dpi=1000)
补充:表降维处理
pandas.melt()方法
案例中原始及降维后的数据框如下:
原始:
Cut Price_A Price_B
0 fair 4300 4000
1 good 3800 3200
2 very good 3950 4300
3 premium 4700 3700
4 ideal 3500 3000
降维处理后:
Cut variable value
0 premium Price_A 4700
1 fair Price_A 4300
2 very good Price_A 3950
3 good Price_A 3800
4 ideal Price_A 3500
5 premium Price_B 3700
6 fair Price_B 4000
7 very good Price_B 4300
8 good Price_B 3200
9 ideal Price_B 3000
文章出处登录后可见!