Обтекание нескольких столбцов / строк в альтаире - PullRequest
0 голосов
/ 04 мая 2018

В ggplot2 легко создать граненый график с гранями, которые охватывают как строки, так и столбцы. Есть ли «гладкий» способ сделать это в 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'
)

но часто я просто хочу отобразить их в сетке, используя более одного столбца / строки, то есть те, которые располагаются в одном столбце / строке, ничего не значат.

Например, см. facet_wrap из ggplot2: http://www.cookbook -r.com / Графики / Facets_ (ggplot2) / # facetwrap

Ответы [ 5 ]

0 голосов
/ 11 июня 2019

В Altair версии 3.1 или новее (выпущен в июне 2019 года) упакованные фасеты поддерживаются непосредственно в 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().

0 голосов
/ 25 февраля 2019

Я обнаружил, что выполнение конкатенации длиной более двух в обоих направлениях приводило к искажению данных и выпадению из окна. Я решил эту проблему путем рекурсивного разбиения массива подзаговоров на квадранты и выполнения чередующихся конкатенаций строк и столбцов. Если у вас нет этой проблемы, хорошо для вас: вы можете использовать одну из простых реализаций, уже опубликованных. Но, если вы это сделаете, я надеюсь, что это поможет.

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
0 голосов
/ 29 августа 2018

Начиная с ответа Рама и используя более функциональный подход, вы также можете попробовать:

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

Таким образом, должно быть легко настроить код и передать некоторые параметры конфигурации всем вашим графикам (например, показать / скрыть галочки, установить одинаковые нижние / верхние ограничения для всех графиков и т. Д.).

0 голосов
/ 12 сентября 2018

Вот общее решение, в котором есть место для добавления слоев. В этом случае 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

enter image description here

0 голосов
/ 04 мая 2018

Вы можете сделать это, указав .repeat() и список переменных row и column. Это ближе к facet_grid() ggplot, чем 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()

Который производит:

enter image description here

Обратите внимание, что весь набор интерактивен в тандеме (увеличение, уменьшение).

Обязательно ознакомьтесь с RepeatedCharts и FacetedCharts в документации.

Создание стиля графиков facet_wrap()

Если вы хотите, чтобы лента диаграмм располагалась одна за другой (не обязательно отображая столбец или строку в переменные в вашем фрейме данных), вы можете сделать это, поместив комбинацию hconcat() и vconcat() в список Альтаирские участки.

Я уверен, что есть более элегантные способы, но я так и сделал.

Логика используется в коде ниже:

  1. Сначала создайте base диаграмму Альтаира
  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

производить:

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...