繁体   English   中英

多列/行刻面包裹在 altair

[英]multiple column/row facet wrap in altair

ggplot2 ,很容易创建一个分面图,其中分面跨越行和列。 altair是否有一种“ altair ”的方法可以做到这altair facet文档

可以在单列中绘制分面图,

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

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'
)

但通常,我只想使用多个列/行将它们绘制在网格中,即排列在单个列/行中的那些并没有特别的意义。

例如,从ggplot2ggplot2http : facet_wrap ( ggplot2 / facet_wrap ggplot2

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

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
)

在此处输入图片说明

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

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

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

您可以通过指定.repeat()以及变量的rowcolumn列表来做到这一点。 这比facet_wrap()更接近 ggplot 的facet_grid() facet_wrap()但 API 非常优雅。 (请参阅此处的讨论。)API 在这里

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()

其中产生:

在此处输入图片说明

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

一定要检查出RepeatedChartsFacetedCharts的文档。

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

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

我相信有更优雅的方法,但我就是这样做的。

下面代码中使用的逻辑:

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

——

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

生产:

在此处输入图片说明

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

numcols=3 # specify the number of columns you want 
all_categories=df['Category_Column'].unique() # array of strings to use as your filters and titles

rows=alt.vconcat(data=df)
numrows=int(np.ceil(len(all_categories) / numcols))
pointer=0
for _ in range(numrows):

  row=all_categories[pointer:pointer+numcols]
  cols=alt.hconcat()

  for a_chart in row:

     # add your layers here
     # line chart
     line=alt.Chart().mark_line(point=True).encode(
        x='variable',
        y='value'
     ).transform_filter(datum.Category_Column == a_chart).properties(
        title=a_chart, height=200, width=200)

     # text labels
     text=alt.Chart().mark_text().encode(
        x='variable', 
        y='value'
     ).transform_filter(datum.Category_Column == a_chart)

     both = line + text
     cols |= both

  rows &= cols
  pointer += numcols

rows

在此处输入图片说明

从 Ram 的回答开始,并使用更实用的方法,您还可以尝试:

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')

22 个图表的分面网格,每行 6 个

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

我发现在任一方向上进行长度大于 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

不要在repeat使用columnrow ,而是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
)

在此处输入图片说明

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM