Seaborn FacetGrid 用于分层计数图?

Posted

技术标签:

【中文标题】Seaborn FacetGrid 用于分层计数图?【英文标题】:Seaborn FacetGrid for stratified countplots? 【发布时间】:2018-03-03 22:01:29 【问题描述】:

注意:full reproduction notebook for this question 可以在 GitHub 上找到。

我有一个数据集,其中包含我想按类分组的 HTTP 响应代码分布。样本数据可以这样生成:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline

mock_http_response_data = pd.DataFrame(
    'response_code':np.repeat([200, 201, 202, 204, 302, 304, 400, 404, 500, 502], 250 ),
)

我在数据中添加了一个基于“响应状态”的列,称为“响应类”。响应类包含与特定响应的类对应的标签:

2xx:成功 3xx:警告 4xx:客户端错误 4xx:服务器错误

判断响应类的函数是:

def determine_response_class(row):    
    response_code = row['response_code']

    if response_code >= 200 and response_code < 300:
        return 'success'
    elif response_code >= 300 and response_code < 400:
        return 'warning'
    elif response_code >= 400 and response_code < 500:
        return 'client_error'
    elif response_code >= 500 and response_code < 600:
        return 'server_error'
    else:
        return 'unknown'

并且该列是这样添加的:

# Add 'Response class' column to API Logs, where response class is determined by HTTP status code
mock_http_response_data['response_class'] = mock_http_response_data.apply(determine_response_class, axis='columns')

“响应状态”(HTTP 状态代码)数据使用基本计数图正确绘制:

sns.countplot(
    x='_source.response_status',
    data=results_df,
    color='teal',
    saturation=0.7)

当我尝试创建计数图的 FacetGrid 时,图表似乎可以工作,但标签不正确:

grid = sns.FacetGrid(mock_http_response_data, col='response_class')

grid.map(sns.countplot, 'response_code')

我希望计数图的 FacetGrid 将具有以下 x 轴标签:

200 201 202 302 304 400 404 500 502

如何创建计数图的 FacetGrid,以便标签正确且分面数据从高到低排序(例如“成功”类列)?

【问题讨论】:

创建一个minimal reproducible example 的问题怎么样?如果您声称的标签不正确,其他人怎么会知道? 问题中的图片描述了数据。第一个图表显示带有正确(x 轴)标签的数据的整体分布,第二个图表只是将数据分成四个部分(2xx、3xx、4xx、5xx)。如果你垂直比较图表,你会发现它们有很强的对应关系,但第二张图片的标签不正确。 我在原始问题中添加了尽可能多的细节,但没有公布实际数据。 好吧,也许你没有明白我的意思。您基本上是在要求某人创建一些数据框来重现该问题,这可能是可能的,但会浪费时间。相反,如果您自己创建一些数据并提供minimal reproducible example,人们会更倾向于帮助您。最后当然是你的选择。 我为这个问题添加了一个完整的复制笔记本,包括数据:github.com/brylie/jupyter_http_status_code_visualization/blob/… 【参考方案1】:

出现标签错误的问题是因为默认情况下,子图的 x 轴是共享的,因此所有图的 x 轴都将与上一个图相同。

您可以使用sharex=False 参数来防止共享轴:

grid = sns.FacetGrid(df, col='class', sharex=False)

import pandas as pd
import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt
import seaborn as sns

codes = [200, 201, 202, 204, 302, 304, 400, 404, 500, 502]
p = np.random.rand(len(codes))
p = p/p.sum()
df = pd.DataFrame( 'code': np.random.choice(codes, size=300, p=p) )

def determine_response_class(row):    
response_code = row['code']

if response_code >= 200 and response_code < 300:
    return 'success'
elif response_code >= 300 and response_code < 400:
    return 'warning'
elif response_code >= 400 and response_code < 500:
    return 'client_error'
elif response_code >= 500 and response_code < 600:
    return 'server_error'
else:
    return 'unknown'

df['class'] = df.apply(determine_response_class, axis='columns')

grid = sns.FacetGrid(df, col='class', sharex=False)

grid.map(sns.countplot, 'code')

plt.show()

排序问题现在是先有鸡还是先有蛋的问题。为了设置列的顺序,您需要知道每个列的计数,这些计数被确定为绘图的一部分。在这一点上,坚持明确区分数据生成、分析和可视化可能是明智之举。下面将显示一个排序图,不使用FacetGrid,首先对数据框中的值进行排序。

import pandas as pd
import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt
import seaborn as sns

codes = [200, 201, 202, 204, 302, 304, 400, 404, 500, 502]
p = np.random.rand(len(codes))
p = p/p.sum()
df = pd.DataFrame( 'code': np.random.choice(codes, size=300, p=p) )

def determine_response_class(row):    
    response_code = row['code']

    if response_code >= 200 and response_code < 300:
        return 'success'
    elif response_code >= 300 and response_code < 400:
        return 'warning'
    elif response_code >= 400 and response_code < 500:
        return 'client_error'
    elif response_code >= 500 and response_code < 600:
        return 'server_error'
    else:
        return 'unknown'

df['class'] = df.apply(determine_response_class, axis='columns')

df2 = df.groupby(["code","class"]).size().reset_index(name="count") \
        .sort_values(by="count", ascending=0).reset_index(drop=True)

fig, axes = plt.subplots(ncols=4, sharey=True, figsize=(8,3))
for ax,(n, group) in zip(axes, df2.groupby("class")):
    sns.barplot(x="code",y="count", data=group, ax=ax, color="C0", order=group["code"])
    ax.set_title(n)

plt.tight_layout()
plt.show()

【讨论】:

以上是关于Seaborn FacetGrid 用于分层计数图?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Seaborn 创建 FacetGrid 堆叠条形图?

如何反转 seaborn 图形级别图的轴(FacetGrid)

在 Seaborn/其他库中聚合的 FacetGrid 图

Seaborn FacetGrid 条形图和色调

如何将单个 vlines 添加到 seaborn FacetGrid 的每个子图

Seaborn FacetGrid - 在最后一个子图之后放置单个颜色条