【问题标题】:Draw a boxplot using an sframe as data source使用 sframe 作为数据源绘制箱线图
【发布时间】:2016-03-19 19:11:27
【问题描述】:

我正在The Billionaire Characteristics Database 数据集上练习我的机器学习分类技能。

我使用sframe 加载和处理数据,seaborn 用于可视化。

在数据分析的过程中,我想画一个按分类变量分组的箱线图,比如seaborn教程中的这个:

在数据集中,有一个 networthusbillion 数值变量和 selfmade 分类变量,用于说明亿万富翁是 self-made 还是他拥有 inherited 美元。

当我尝试使用sns.boxplot(x='selfmade', y='networthusbillion', data=data) 绘制类似的箱线图时,它会抛出以下错误:

---------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-17-f4bd651c2ae7> in <module>()
----> 1 sns.boxplot(x='selfmade', y='networthusbillion', data=billionaires)

/home/iulian/.virtualenvs/data-science-python2/lib/python2.7/site-packages/seaborn/categorical.pyc in boxplot(x, y, hue, data, order, hue_order, orient, color, palette, saturation, width, fliersize, linewidth, whis, notch, ax, **kwargs)
   2127     plotter = _BoxPlotter(x, y, hue, data, order, hue_order,
   2128                           orient, color, palette, saturation,
-> 2129                           width, fliersize, linewidth)
   2130 
   2131     if ax is None:

/home/iulian/.virtualenvs/data-science-python2/lib/python2.7/site-packages/seaborn/categorical.pyc in __init__(self, x, y, hue, data, order, hue_order, orient, color, palette, saturation, width, fliersize, linewidth)
    420                  width, fliersize, linewidth):
    421 
--> 422         self.establish_variables(x, y, hue, data, orient, order, hue_order)
    423         self.establish_colors(color, palette, saturation)
    424 

/home/iulian/.virtualenvs/data-science-python2/lib/python2.7/site-packages/seaborn/categorical.pyc in establish_variables(self, x, y, hue, data, orient, order, hue_order, units)
    136             # See if we need to get variables from `data`
    137             if data is not None:
--> 138                 x = data.get(x, x)
    139                 y = data.get(y, y)
    140                 hue = data.get(hue, hue)

AttributeError: 'SFrame' object has no attribute 'get'

我尝试了以下形式来绘制箱线图-它们都没有达到结果:

sns.boxplot(x=billionaires['selfmade'], y=billionaires['networthusbillion'])
sns.boxplot(x='selfmade', y='networthusbillion', data=billionaires['selfmade', 'networthusbillion'])

但是,我可以使用sframe 绘制箱形图,但不按selfmade 分组:

sns.boxplot(x=billionaires['networthusbillion'])

所以,我的问题是: 有没有办法使用sframe 绘制按分类变量分组的箱线图?也许我做错了什么?

顺便说一句,我设法使用 pandas.DataFrame 使用相同的语法 (sns.boxplot(x='selfmade', y='networthusbillion', data=data)) 绘制它,所以使用 sframeseaborn 进行分组可能还没有实现。

【问题讨论】:

    标签: python seaborn sframe


    【解决方案1】:

    TL;DR

    使用sframeseaborn 进行分组尚未实现。


    在深入研究 seaborn 的源代码后,我发现它是专门为 pandas.DataFrame 设计的。在他们的回答中接受 absoluteNoWarranty 的建议,我得到了以下错误:

    TypeError: __getitem__() takes exactly 2 arguments (3 given)
    

    看看get函数调用中的args,有这样的数据:

    ('gender', 'gender')
    

    发生这种情况是因为BoxPlot 的源代码中的这段代码:

    # See if we need to get variables from `data`
    if data is not None:
        x = data.get(x, x)
        y = data.get(y, y)
        hue = data.get(hue, hue)
        units = data.get(units, units)
    

    它会尝试获取该值并使用与备用值相同的值,以防万一它不存在。这会导致__getitem__() 出现错误,因为它是使用(self, 'gender', 'gender') 参数调用的。

    我尝试重写get()函数如下:

    def get(self, *args):
        return self.__getitem__(args[0]) if args[0] else None  # The `None` is here because the `units` in the source code is `None` for boxplots.
    

    在这里我得到了结束我尝试的错误:

    TypeError: 'SArray' object is not callable
    

    查看源代码,检查y数据是否为pd.Series,如果不是,则将y值转换为1:

    if not isinstance(vals, pd.Series):
        vals = pd.Series(vals)
    
    # Group the val data
    grouped_vals = vals.groupby(grouper)
    

    当执行vals.groupby(grouper)(grouper 仍然是SArray 实例)时,它会进入pandas 核心工作,在此调用SArray 并抛出错误。故事结束。

    【讨论】:

    • 我编辑了我的答案。看看它是否有帮助(尽管此时它可能太老套了。)
    【解决方案2】:

    问题是sns.boxplot 期望数据有一个get 方法,就像 Pandas 的 Dataframe。在 Pandas 中,get 方法返回单列,因此它与括号索引相同,即 your_df['your_column_name']

    解决此问题的最简单方法是在 sframe 上调用 to_dataframe 方法将其转换为数据帧。

    sns.boxplot(x='selfmade', y='networthusbillion', data=data.to_dataframe())
    

    或者,您可以通过编写类包装器或在 SFrame 类上使用 monkey-patching get 来解决问题。

    import numpy as np
    import sframe
    import pandas as pd
    import seaborn as sns
    import matplotlib.pyplot as plt
    
    # For demostration purposes
    def to_sframe(df):
        import sframe
        d = {}
        for key in df.keys():
            d[key] = list(df[key])
        return sframe.SFrame(d)
    pd.DataFrame.to_sframe = to_sframe
    
    tips = sns.load_dataset('tips')
    
    # Monkey patch sframe's get and _CategoricalPlotter's _group_longform
    def get(self, *args):
        key = args[0]
        return self.__getitem__(key) if key else None
    sframe.SFrame.get = get
    
    
    def _group_longform(self, vals, grouper, order):
        """Group a long-form variable by another with correct order."""
        #import pdb;pdb.set_trace()
    
        if type(vals) == sframe.SArray:
            _sf = sframe.SFrame({'vals':vals, 'grouper':grouper})
            grouped_vals = _sf.groupby('grouper', sframe.aggregate.CONCAT('vals'))
            out_data = []
            for g in order:
                try:
                    g_vals = np.asarray(grouped_vals.filter_by(g, 'grouper')["List of vals"][0])
                except KeyError:
                    g_vals = np.array([])
                out_data.append(g_vals)
            label = ""
            return out_data, label
    
        ## Code copied from original _group_longform
        # Ensure that the groupby will work
        if not isinstance(vals, pd.Series):
            vals = pd.Series(vals)
    
        # Group the val data
        grouped_vals = vals.groupby(grouper)
        out_data = []
        for g in order:
            try:
                g_vals = np.asarray(grouped_vals.get_group(g))
            except KeyError:
                g_vals = np.array([])
            out_data.append(g_vals)
    
        # Get the vals axis label
        label = vals.name
    
        return out_data, label
    
    sns.categorical._CategoricalPlotter._group_longform = _group_longform
    
    
    # Plots should be equivalent
    #1.
    plt.figure()
    sns.boxplot(x="day", y="total_bill", data=tips)
    #2.
    plt.figure()
    sns.boxplot(x="day", y="total_bill", data=tips.to_sframe(),
                order=["Thur", "Fri", "Sat", "Sun"])
    plt.xlabel("day")
    plt.ylabel("total_bill")
    
    plt.show()
    

    【讨论】:

    • 感谢您的回答。您提供的解决方法是有效的,但我需要调查to_dataframe() 转换的成本有多大。然而,猴子修补不起作用。我深入研究了seaborn 源代码,它的方法专门用于数据帧。
    • 这是来自sframe documentationto_dataframe() 的快速回答:“此操作将在内存中构造一个pandas.DataFrame。当返回对象的大小很大时必须小心。”所以,不幸的是,这也不是一个有效的选择。
    猜你喜欢
    • 2017-12-16
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多