使用 sframe 作为数据源绘制箱线图
Draw a boxplot using an sframe as data source
我正在 The Billionaire Characteristics Database 数据集上练习我的 ML 分类技能。
我正在使用 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'
我尝试了以下表格来绘制箱线图 - none 取得了结果:
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
绘制按分类变量分组的箱线图?也许我做错了什么?
顺便说一下,我设法使用相同语法 (sns.boxplot(x='selfmade', y='networthusbillion', data=data)
) 使用 pandas.DataFrame
绘制它,所以可能使用 sframe
和 seaborn
进行分组只是还没有实施。
问题是 sns.boxplot
期望数据具有 get
方法,如 Pandas' Dataframe。在 Pandas 中,get
方法 returns 单列,因此它与括号索引相同,即 your_df['your_column_name']
.
解决此问题的最简单方法是调用 sframe 上的 to_dataframe
方法将其转换为数据帧。
sns.boxplot(x='selfmade', y='networthusbillion', data=data.to_dataframe())
或者,您可以通过在 SFrame class.
上编写 class 包装器或使用 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()
TL;DR
使用 sframe
和 seaborn
的分组尚未实现。
在深入研究 seaborn 的源代码后,我发现它是专门为 pandas.DataFrame
设计的。在他们的回答中采纳了 absolutelyNoWarranty 的建议,我得到了以下错误:
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
值转换为一个:
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
被调用并抛出错误.故事结束。
我正在 The Billionaire Characteristics Database 数据集上练习我的 ML 分类技能。
我正在使用 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'
我尝试了以下表格来绘制箱线图 - none 取得了结果:
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
绘制按分类变量分组的箱线图?也许我做错了什么?
顺便说一下,我设法使用相同语法 (sns.boxplot(x='selfmade', y='networthusbillion', data=data)
) 使用 pandas.DataFrame
绘制它,所以可能使用 sframe
和 seaborn
进行分组只是还没有实施。
问题是 sns.boxplot
期望数据具有 get
方法,如 Pandas' Dataframe。在 Pandas 中,get
方法 returns 单列,因此它与括号索引相同,即 your_df['your_column_name']
.
解决此问题的最简单方法是调用 sframe 上的 to_dataframe
方法将其转换为数据帧。
sns.boxplot(x='selfmade', y='networthusbillion', data=data.to_dataframe())
或者,您可以通过在 SFrame class.
上编写 class 包装器或使用 monkey-patchingget
来绕过问题
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()
TL;DR
使用 sframe
和 seaborn
的分组尚未实现。
在深入研究 seaborn 的源代码后,我发现它是专门为 pandas.DataFrame
设计的。在他们的回答中采纳了 absolutelyNoWarranty 的建议,我得到了以下错误:
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
值转换为一个:
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
被调用并抛出错误.故事结束。