altair中的多列/行facet包装

时间:2018-05-03 21:18:59

标签: python plot facet facet-wrap altair

ggplot2中,可以轻松创建带有跨越行和列的构面的分面图。在altair中有一种“光滑”的方法吗? facet documentation

可以在一列中绘制构面图,

chart = alt.Chart(iris).mark_point().encode(
    x='petalLength:Q',
    y='petalWidth:Q',
    color='species:N'
).properties(
    width=180,
    height=180
).facet(
    row='species:N'
)

并且在一行中,

chart = alt.Chart(iris).mark_point().encode(
    x='petalLength:Q',
    y='petalWidth:Q',
    color='species:N'
).properties(
    width=180,
    height=180
).facet(
    column='species:N'
)

但通常情况下,我只想使用多个列/行在网格中绘制它们,即在单个列/行中排列的那些并不特别具有任何意义。

例如,请参阅facet_wrap中的ggplot2http://www.cookbook-r.com/Graphs/Facets_(ggplot2)/#facetwrap

6 个答案:

答案 0 :(得分:5)

您可以通过指定.repeat()以及rowcolumn变量列表来执行此操作。这比ggplot facet_grid()更接近facet_wrap(),但API非常优雅。 (请参阅讨论here。)API为here

iris = data.iris()

alt.Chart(iris).mark_circle().encode(
    alt.X(alt.repeat("column"), type='quantitative'),
    alt.Y(alt.repeat("row"), type='quantitative'),
    color='species:N'
).properties(
    width=250,
    height=250
).repeat(
    row=['petalLength', 'petalWidth'],
    column=['sepalLength', 'sepalWidth']
).interactive()

产生:

enter image description here

请注意,整个集合是串联的交互式(放大,缩小)。

请务必查看文档中的RepeatedChartsFacetedCharts

创建facet_wrap()样式的图表网格

如果您想要一个接一个地排列的图表功能区(不一定将列或行映射到数据框中的变量),您可以通过包装hconcat()vconcat()的组合来实现在Altair地块列表上。

我确信有更优雅的方式,但这就是我做到的。

以下代码中使用的逻辑:

  1. 首先,创建一个base Altair图表
  2. 使用transform_filter()将您的数据过滤为多个子图
  3. 确定一行中的绘图数量并将该列表切片
  4. 遍历列表列表,一次放下一行。
  5. -

    import altair as alt
    from vega_datasets import data
    from altair.expr import datum
    
    iris = data.iris()
    
    base = alt.Chart(iris).mark_point().encode(
        x='petalLength:Q',
        y='petalWidth:Q',
        color='species:N'
    ).properties(
        width=60,
        height=60
    )
    
    #create a list of subplots
    subplts = []
    for pw in iris['petalWidth'].unique():
        subplts.append(base.transform_filter(datum.petalWidth == pw))
    
    
    def facet_wrap(subplts, plots_per_row):
        rows = [subplts[i:i+plots_per_row] for i in range(0, len(subplts), plots_per_row)]
        compound_chart = alt.hconcat()
        for r in rows:
            rowplot = alt.vconcat() #start a new row
            for item in r:
                rowplot |= item #add suplot to current row as a new column
            compound_chart &= rowplot # add the entire row of plots as a new row
        return compound_chart
    
    
    compound_chart = facet_wrap(subplts, plots_per_row=6)    
    compound_chart
    

    生产:

    enter image description here

答案 1 :(得分:3)

在Altair 3.1版或更高版本(2019年6月发布)中,直接在Altair API中支持包装的构面。修改您的虹膜示例,您可以将构面包装在两列中,如下所示:

import altair as alt
from vega_datasets import data
iris = data.iris()

alt.Chart(iris).mark_point().encode(
    x='petalLength:Q',
    y='petalWidth:Q',
    color='species:N'
).properties(
    width=180,
    height=180
).facet(
    facet='species:N',
    columns=2
)

enter image description here

或者,可以使用构面指定相同的图表作为编码:

alt.Chart(iris).mark_point().encode(
    x='petalLength:Q',
    y='petalWidth:Q',
    color='species:N',
    facet='species:N'
).properties(
    width=180,
    height=180,
    columns=2
)

可以类似地为alt.concat()中的串联图表和重复图表alt.Chart.repeat()中指定column参数。

答案 2 :(得分:1)

从Ram的answer开始,并使用更具功能性的方法,您还可以尝试:

import altair as alt
from vega_datasets import data
from altair.expr import datum

iris = data.iris()

base = alt.Chart(iris).mark_point().encode(
    x='petalLength:Q',
    y='petalWidth:Q',
    color='species:N'
)

# chart factory
def make_chart(base_chart, pw, options):
    title = 'Petal Width {:.2f}'.format(pw)
    chart = base_chart\
      .transform_filter(datum.petalWidth == pw)\
      .properties(width=options['width'], height=options['height'], title=title)
    return chart

# create all charts
options = {'width': 50, 'height': 60}
charts = [make_chart(base, pw, options) for pw in sorted(iris['petalWidth'].unique())]

# make a single row
def make_hcc(row_of_charts):
    hconcat = [chart for chart in row_of_charts]
    hcc = alt.HConcatChart(hconcat=hconcat)
    return hcc

# take an array of charts and produce a facet grid
def facet_wrap(charts, charts_per_row):
    rows_of_charts = [
        charts[i:i+charts_per_row] 
        for i in range(0, len(charts), charts_per_row)]        
    vconcat = [make_hcc(r) for r in rows_of_charts]    
    vcc = alt.VConcatChart(vconcat=vconcat)\
      .configure_axisX(grid=True)\
      .configure_axisY(grid=True)
    return vcc

# assemble the facet grid
compound_chart = facet_wrap(charts, charts_per_row=6)
compound_chart.properties(title='My Facet grid')

Facet grid of 22 charts, 6 per row

这样一来,调整代码并将一些配置选项传递给所有绘图(例如显示/隐藏刻度,为所有绘图设置相同的底部/顶部限制等)应该很容易。

答案 3 :(得分:0)

这是一个通用解决方案,可以添加图层。在这种情况下,DataFrame具有三列,并且是长格式。

inUsd

enter image description here

答案 4 :(得分:0)

我发现在两个方向上的长度都大于2的串联会导致数据变形并掉出窗口。我通过将子图数组递归分解为象限并进行行和列的交替连接来解决此问题。如果您没有这个问题,对您有好处:您可以使用已经发布的更简单的实现之一。但是,如果您愿意,我希望这会有所帮助。

def facet_wrap(subplots, plots_per_row):
    # base cases
    if len(subplots) == 0 or plots_per_row == 0:
        return None
    if len(subplots) == 1:
        return subplots[0]

    # split subplots list into quadrants
    # we always fill top and left first
    quadrants = [[], [], [], []] # tl, tr, bl, br
    for subplot_index, subplot in enumerate(subplots):
        right_half = (subplot_index % plots_per_row) >= plots_per_row // 2
        lower_half = subplot_index >= len(subplots) / 2
        quadrants[2 * lower_half + right_half].append(subplot)

    # recurse on each quadrant
    # we want a single chart or None in place of each quadrant
    m = plots_per_row % 2 # if plots_per_row is odd then we need to split it unevenly
    quadplots = [
        facet_wrap(q, plots_per_row // 2 + m * (0 == (i % 2))) \
        for i, q in enumerate(quadrants)
    ]

    # join the quadrants
    rows = [quadplots[:2], quadplots[2:]]
    colplot = alt.hconcat()
    for row in rows:
        rowplot = alt.vconcat()
        for item in row:
            if item != None:
                rowplot = rowplot | item
        colplot &= rowplot
    return colplot

答案 5 :(得分:0)

不要在 column 中使用 rowrepeat,而是使用 repeat 如下:

import altair as alt
from vega_datasets import data

cars = data.cars.url

alt.Chart(cars, width=200, height=150).mark_bar().encode(
    x=alt.X(alt.repeat('repeat'), type='quantitative', bin=alt.Bin(maxbins=20)),
    y='count()'
).repeat(
    repeat=["Horsepower", "Miles_per_Gallon", "Acceleration", "Displacement"], 
    columns=2
)

enter image description here