Làm cách nào để gắn nhãn Y ticklabels thành nhóm / danh mục trong clustermap seaborn?


8

Tôi muốn tạo một cụm bản đồ / bản đồ nhiệt của dữ liệu hiện diện gen từ các bệnh nhân trong đó các gen sẽ được nhóm thành các loại (ví dụ như chemotaxis, endotoxin, v.v.) và được dán nhãn thích hợp. Tôi đã không tìm thấy bất kỳ tùy chọn như vậy trong tài liệu seaborn. Tôi biết cách tạo bản đồ nhiệt, tôi chỉ không biết cách gắn nhãn yticks làm danh mục. Đây là một mẫu (không liên quan đến công việc của tôi) về những gì tôi muốn đạt được:

bản đồ nhiệt

Ở đây, yticklabels tháng một, tháng hai và tháng ba được đưa ra nhãn nhóm mùa đông và các yticklabels khác cũng được dán nhãn tương tự.


Bạn đang cố gắng tạo một dendrogram (tức là vẫn còn tháng một, tháng hai, tháng ba và một nút gọi là "mùa đông" xuất hiện phía trên nó)? Hay bạn đang cố gắng để thoát khỏi các tháng và thay vào đó là mùa?
gnahum

Không phải là một chương trình dendro. Tôi không muốn phân cụm các hàng (tức là tháng 1, tháng 2, v.v.), tôi muốn giữ chúng theo thứ tự chúng xuất hiện trong khung dữ liệu. Tôi chỉ muốn gắn nhãn tháng (tức là tháng một, tháng hai, tháng ba là mùa đông).
Ahmed Abdullah

@gnahum Không. Tôi cũng không muốn thay thế. Tôi muốn tạo ra một hình ảnh giống như hình đã cho (tất nhiên là được đánh bóng :))
Ahmed Abdullah

bạn có thể vượt qua một danh sách mới được thành lập? tức là `` `sns.heatmap (df, yticklabels = [ 'mùa đông', Không, Không, 'mùa xuân', Không, Không, 'mùa hè', Không, Không, 'mùa thu', Không có, Không])` ``
gnahum

@gnahum Điều đó chỉ đơn giản là thay thế tên tháng. Nhưng tôi không muốn thay thế chúng.
Ahmed Abdullah

Câu trả lời:


2

Tôi đã sao chép ví dụ mà bạn đã đưa ra khi đi biển, điều chỉnh câu trả lời của @ Stein từ đây .

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from itertools import groupby
import datetime
import seaborn as sns

def test_table():
    months = [datetime.date(2008, i+1, 1).strftime('%B') for i in range(12)]
    seasons = ['Winter',]*3 + ['Spring',]*2 + ['Summer']*3 + ['Pre-Winter',]*4
    tuples = list(zip(months, seasons))
    index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
    d = {i: [np.random.randint(0,50) for _ in range(12)] for i in range(1950, 1960)}
    df = pd.DataFrame(d, index=index)
    return df

def add_line(ax, xpos, ypos):
    line = plt.Line2D([ypos, ypos+ .2], [xpos, xpos], color='black', transform=ax.transAxes)
    line.set_clip_on(False)
    ax.add_line(line)

def label_len(my_index,level):
    labels = my_index.get_level_values(level)
    return [(k, sum(1 for i in g)) for k,g in groupby(labels)]

def label_group_bar_table(ax, df):
    xpos = -.2
    scale = 1./df.index.size
    for level in range(df.index.nlevels):
        pos = df.index.size
        for label, rpos in label_len(df.index,level):
            add_line(ax, pos*scale, xpos)
            pos -= rpos
            lypos = (pos + .5 * rpos)*scale
            ax.text(xpos+.1, lypos, label, ha='center', transform=ax.transAxes) 
        add_line(ax, pos*scale , xpos)
        xpos -= .2

df = test_table()

fig = plt.figure(figsize = (10, 10))
ax = fig.add_subplot(111)
sns.heatmap(df)

#Below 3 lines remove default labels
labels = ['' for item in ax.get_yticklabels()]
ax.set_yticklabels(labels)
ax.set_ylabel('')

label_group_bar_table(ax, df)
fig.subplots_adjust(bottom=.1*df.index.nlevels)
plt.show()

Cung cấp:

Mong rằng sẽ giúp.


Điều này dường như không hoạt động. Đây là những gì tôi nhận được. drive.google.com/open?id=1SRbVe9Bk25xiplkn64sZXfbruUrqt5Ro
Ahmed Abdullah

Thật kỳ lạ, tôi không biết tại sao điều đó lại xảy ra - nó giống như bộ ký tự được sử dụng để tạo nhãn biểu đồ không bao gồm bảng chữ cái Latin vì một số lý do. Điều gì xảy ra nếu bạn thay đổi nhãn nhóm trong hàm test_table?
CDJB

Thay đổi bảng chữ cái trong hàm test_table vẫn giữ nguyên đầu ra.
Ahmed Abdullah

Tôi đang làm điều này trong python 3.6.7.
Ahmed Abdullah

1
Tôi đã cập nhật matplotlib lên 3.1.2 để sửa lỗi trong matplotlib 3.1.1 bằng các bản đồ nhiệt - các dòng hiện phù hợp với dữ liệu; xem đầu ra ví dụ mới.
CDJB

2

Tôi chưa thử nghiệm điều này với seaborn chưa, nhưng những công việc sau đây với vani matplotlib.

nhập mô tả hình ảnh ở đây

#!/usr/bin/env python
"""
Annotate a group of y-tick labels as such.
"""

import matplotlib.pyplot as plt
from matplotlib.transforms import TransformedBbox

def annotate_yranges(groups, ax=None):
    """
    Annotate a group of consecutive yticklabels with a group name.

    Arguments:
    ----------
    groups : dict
        Mapping from group label to an ordered list of group members.
    ax : matplotlib.axes object (default None)
        The axis instance to annotate.
    """
    if ax is None:
        ax = plt.gca()

    label2obj = {ticklabel.get_text() : ticklabel for ticklabel in ax.get_yticklabels()}

    for ii, (group, members) in enumerate(groups.items()):
        first = members[0]
        last = members[-1]

        bbox0 = _get_text_object_bbox(label2obj[first], ax)
        bbox1 = _get_text_object_bbox(label2obj[last], ax)

        set_yrange_label(group, bbox0.y0 + bbox0.height/2,
                         bbox1.y0 + bbox1.height/2,
                         min(bbox0.x0, bbox1.x0),
                         -2,
                         ax=ax)


def set_yrange_label(label, ymin, ymax, x, dx=-0.5, ax=None, *args, **kwargs):
    """
    Annotate a y-range.

    Arguments:
    ----------
    label : string
        The label.
    ymin, ymax : float, float
        The y-range in data coordinates.
    x : float
        The x position of the annotation arrow endpoints in data coordinates.
    dx : float (default -0.5)
        The offset from x at which the label is placed.
    ax : matplotlib.axes object (default None)
        The axis instance to annotate.
    """

    if not ax:
        ax = plt.gca()

    dy = ymax - ymin
    props = dict(connectionstyle='angle, angleA=90, angleB=180, rad=0',
                 arrowstyle='-',
                 shrinkA=10,
                 shrinkB=10,
                 lw=1)
    ax.annotate(label,
                xy=(x, ymin),
                xytext=(x + dx, ymin + dy/2),
                annotation_clip=False,
                arrowprops=props,
                *args, **kwargs,
    )
    ax.annotate(label,
                xy=(x, ymax),
                xytext=(x + dx, ymin + dy/2),
                annotation_clip=False,
                arrowprops=props,
                *args, **kwargs,
    )


def _get_text_object_bbox(text_obj, ax):
    # https://stackoverflow.com/a/35419796/2912349
    transform = ax.transData.inverted()
    # the figure needs to have been drawn once, otherwise there is no renderer?
    plt.ion(); plt.show(); plt.pause(0.001)
    bb = text_obj.get_window_extent(renderer = ax.get_figure().canvas.renderer)
    # handle canvas resizing
    return TransformedBbox(bb, transform)


if __name__ == '__main__':

    import numpy as np

    fig, ax = plt.subplots(1,1)

    # so we have some extra space for the annotations
    fig.subplots_adjust(left=0.3)

    data = np.random.rand(10,10)
    ax.imshow(data)

    ticklabels = 'abcdefghij'
    ax.set_yticks(np.arange(len(ticklabels)))
    ax.set_yticklabels(ticklabels)

    groups = {
        'abc' : ('a', 'b', 'c'),
        'def' : ('d', 'e', 'f'),
        'ghij' : ('g', 'h', 'i', 'j')
    }

    annotate_yranges(groups)

    plt.show()

Giải pháp này hoạt động với bản đồ nhiệt biển quá! cảm ơn.
Ahmed Abdullah
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.