Mapa cieplna w matplotlib z pcolor?

100

Chciałbym zrobić taką mapę cieplną (pokazaną na FlowingData ): Mapa ciepła

Dane źródłowe są tutaj , ale losowe dane i etykiety byłyby w porządku, tj

import numpy
column_labels = list('ABCD')
row_labels = list('WXYZ')
data = numpy.random.rand(4,4)

Tworzenie mapy cieplnej jest dość łatwe w matplotlib:

from matplotlib import pyplot as plt
heatmap = plt.pcolor(data)

I nawet znalazłem argumenty w mapie kolorów, które wyglądają dobrze:heatmap = plt.pcolor(data, cmap=matplotlib.cm.Blues)

Ale poza tym nie mogę dowiedzieć się, jak wyświetlać etykiety dla kolumn i wierszy oraz wyświetlać dane w odpowiedniej orientacji (początek w lewym górnym rogu zamiast w lewym dolnym rogu).

Wszystkie próby manipulacji heatmap.axes(np. heatmap.axes.set_xticklabels = column_labels) Zawiodły. Czego tu brakuje?

Jason Sundram
źródło
To pytanie z mapy popularności w dużym stopniu się pokrywa - może tam być dla Ciebie dobra informacja.
John Lyon
Techniki etykiet z tego postu mogą pomóc stackoverflow.com/questions/6352740/matplotlib-label-each-bin
tacaswell

Odpowiedzi:

123

To już późno, ale oto moja implementacja mapy cieplnej NBA flowingdata w języku Python.

aktualizacja: 1/4/2014 : dzięki wszystkim

# -*- coding: utf-8 -*-
# <nbformat>3.0</nbformat>

# ------------------------------------------------------------------------
# Filename   : heatmap.py
# Date       : 2013-04-19
# Updated    : 2014-01-04
# Author     : @LotzJoe >> Joe Lotz
# Description: My attempt at reproducing the FlowingData graphic in Python
# Source     : http://flowingdata.com/2010/01/21/how-to-make-a-heatmap-a-quick-and-easy-solution/
#
# Other Links:
#     http://stackoverflow.com/questions/14391959/heatmap-in-matplotlib-with-pcolor
#
# ------------------------------------------------------------------------

import matplotlib.pyplot as plt
import pandas as pd
from urllib2 import urlopen
import numpy as np
%pylab inline

page = urlopen("http://datasets.flowingdata.com/ppg2008.csv")
nba = pd.read_csv(page, index_col=0)

# Normalize data columns
nba_norm = (nba - nba.mean()) / (nba.max() - nba.min())

# Sort data according to Points, lowest to highest
# This was just a design choice made by Yau
# inplace=False (default) ->thanks SO user d1337
nba_sort = nba_norm.sort('PTS', ascending=True)

nba_sort['PTS'].head(10)

# Plot it out
fig, ax = plt.subplots()
heatmap = ax.pcolor(nba_sort, cmap=plt.cm.Blues, alpha=0.8)

# Format
fig = plt.gcf()
fig.set_size_inches(8, 11)

# turn off the frame
ax.set_frame_on(False)

# put the major ticks at the middle of each cell
ax.set_yticks(np.arange(nba_sort.shape[0]) + 0.5, minor=False)
ax.set_xticks(np.arange(nba_sort.shape[1]) + 0.5, minor=False)

# want a more natural, table-like display
ax.invert_yaxis()
ax.xaxis.tick_top()

# Set the labels

# label source:https://en.wikipedia.org/wiki/Basketball_statistics
labels = [
    'Games', 'Minutes', 'Points', 'Field goals made', 'Field goal attempts', 'Field goal percentage', 'Free throws made', 'Free throws attempts', 'Free throws percentage',
    'Three-pointers made', 'Three-point attempt', 'Three-point percentage', 'Offensive rebounds', 'Defensive rebounds', 'Total rebounds', 'Assists', 'Steals', 'Blocks', 'Turnover', 'Personal foul']

# note I could have used nba_sort.columns but made "labels" instead
ax.set_xticklabels(labels, minor=False)
ax.set_yticklabels(nba_sort.index, minor=False)

# rotate the
plt.xticks(rotation=90)

ax.grid(False)

# Turn off all the ticks
ax = plt.gca()

for t in ax.xaxis.get_major_ticks():
    t.tick1On = False
    t.tick2On = False
for t in ax.yaxis.get_major_ticks():
    t.tick1On = False
    t.tick2On = False

Wynik wygląda następująco: mapa cieplna NBA podobna do danych

Jest takie notebook ipython ze wszystkimi tego kodu tutaj . Wiele się nauczyłem od „przepełnienia”, więc mam nadzieję, że ktoś uzna to za przydatne.

BubbleGuppies
źródło
1
Powyższy kod nie działał w notebooku iPythnon. Dokonałem niewielkiej modyfikacji, zmieniając nba_sort = nba_norm.sort ('PTS', ascending = True, inplace = True) na nba_sort = nba_norm.copy () nba_sort.sort ('PTS', rosnąco = True, inplace = True) ponieważ sortowanie działa przez efekt uboczny, a nie przez powrót funkcji! Dzięki za wspaniały przykład pomysłu!
Yu Shen
1
Hmmm ... wydaje się, że masz rację. Nie jestem pewien, o co w tym wszystkim chodzi. Poprawię kod. Dzięki!
BubbleGuppies
Jaki byłby najłatwiejszy sposób na wykonanie takiej grafiki, ale wyświetlenie wartości statystyki w tabeli. To znaczy, chcę zrobić coś pcolortakiego, ale ma też pokazane wartości liczbowe. LUB: Chcę stworzyć matplotlib, tablektóry koloruje swoje komórki. Widziałem rozwiązania drugiego problemu i są one brzydkie estetycznie. Wygląda to świetnie, gdybym tylko wiedział, jak nałożyć cyfry.
8one6
Tak. Natknąłem się na to, odpowiadając na czyjeś pytanie: stackoverflow.com/a/21167108/2501018
8one6
@joelotz Czy byłbyś skłonny wnieść (zmodyfikowaną) wersję tego do dokumentacji matplotlib? Jeśli tak, po prostu otwórz komunikat PR lub wyślij mi wiadomość e-mail (zobacz mój profil).
tacaswell
12

Moduł python seaborn jest oparty na matplotlib i tworzy bardzo ładną mapę cieplną.

Poniżej znajduje się realizacja z seabornem, zaprojektowana dla notebooka ipython / jupyter.

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
# import the data directly into a pandas dataframe
nba = pd.read_csv("http://datasets.flowingdata.com/ppg2008.csv", index_col='Name  ')
# remove index title
nba.index.name = ""
# normalize data columns
nba_norm = (nba - nba.mean()) / (nba.max() - nba.min())
# relabel columns
labels = ['Games', 'Minutes', 'Points', 'Field goals made', 'Field goal attempts', 'Field goal percentage', 'Free throws made', 
          'Free throws attempts', 'Free throws percentage','Three-pointers made', 'Three-point attempt', 'Three-point percentage', 
          'Offensive rebounds', 'Defensive rebounds', 'Total rebounds', 'Assists', 'Steals', 'Blocks', 'Turnover', 'Personal foul']
nba_norm.columns = labels
# set appropriate font and dpi
sns.set(font_scale=1.2)
sns.set_style({"savefig.dpi": 100})
# plot it out
ax = sns.heatmap(nba_norm, cmap=plt.cm.Blues, linewidths=.1)
# set the x-axis labels on the top
ax.xaxis.tick_top()
# rotate the x-axis labels
plt.xticks(rotation=90)
# get figure (usually obtained via "fig,ax=plt.subplots()" with matplotlib)
fig = ax.get_figure()
# specify dimensions and save
fig.set_size_inches(15, 20)
fig.savefig("nba.png")

Wynik wygląda następująco: Mapa termiczna NBA Seaaborn użyłem mapy kolorów matplotlib Blues, ale osobiście uważam, że domyślne kolory są całkiem piękne. Użyłem matplotlib do obrócenia etykiet osi X, ponieważ nie mogłem znaleźć składni morskiej. Jak zauważył grexor, konieczne było określenie wymiarów (rys. Set_size_inches) metodą prób i błędów, co było trochę frustrujące.

Jak zauważył Paul H, możesz łatwo dodać wartości do map ciepła (adnotacja = True), ale w tym przypadku nie sądziłem, że poprawiło to wartość. Kilka fragmentów kodu zostało zaczerpniętych z doskonałej odpowiedzi Joelotza.

Mark Teese
źródło
11

Głównym problemem jest to, że najpierw musisz ustawić lokalizację swoich znaczników x i y. Ponadto matplotlib pomaga używać bardziej zorientowanego obiektowo interfejsu. Mianowicie wejdź w interakcję axesbezpośrednio z obiektem.

import matplotlib.pyplot as plt
import numpy as np
column_labels = list('ABCD')
row_labels = list('WXYZ')
data = np.random.rand(4,4)
fig, ax = plt.subplots()
heatmap = ax.pcolor(data)

# put the major ticks at the middle of each cell, notice "reverse" use of dimension
ax.set_yticks(np.arange(data.shape[0])+0.5, minor=False)
ax.set_xticks(np.arange(data.shape[1])+0.5, minor=False)


ax.set_xticklabels(row_labels, minor=False)
ax.set_yticklabels(column_labels, minor=False)
plt.show()

Mam nadzieję, że to pomoże.

Paul H.
źródło
Dzięki, @Paul H, to działa pięknie. Korzystałem z heatmap.axesnieruchomości, która z jakiegoś powodu nic nie robi.
Jason Sundram
Czy wiesz, jak przesunąć etykiety osi X, aby znalazły się na górze? Próbowałem oczywistego ax.xaxis.set_label_position('top')bezskutecznie.
Jason Sundram
@JasonSundram Powinieneś otworzyć nowe pytanie o przesunięcie pozycjonowania etykiety, bo to powinno działać i dziwne, że nie.
tacaswell
1
@tcaswell, dobra uwaga. Nowe pytanie tutaj: stackoverflow.com/questions/14406214/…
Jason Sundram
1
@ Tgsmith61591 Użyłbym funkcji mapy cieplnej seaborn, ustawiając annot=Truepo wywołaniu ( stanford.edu/~mwaskom/software/seaborn/generated/… )
Paul H
3

Ktoś zredagował to pytanie, aby usunąć kod, którego użyłem, więc byłem zmuszony dodać to jako odpowiedź. Dziękujemy wszystkim, którzy wzięli udział w udzieleniu odpowiedzi na to pytanie! Myślę, że większość innych odpowiedzi jest lepszych niż ten kod, zostawiam to tutaj w celach informacyjnych.

Dzięki Paulowi H. i unutbu (który odpowiedział na to pytanie ), mam całkiem ładnie wyglądające wyjście:

import matplotlib.pyplot as plt
import numpy as np
column_labels = list('ABCD')
row_labels = list('WXYZ')
data = np.random.rand(4,4)
fig, ax = plt.subplots()
heatmap = ax.pcolor(data, cmap=plt.cm.Blues)

# put the major ticks at the middle of each cell
ax.set_xticks(np.arange(data.shape[0])+0.5, minor=False)
ax.set_yticks(np.arange(data.shape[1])+0.5, minor=False)

# want a more natural, table-like display
ax.invert_yaxis()
ax.xaxis.tick_top()

ax.set_xticklabels(row_labels, minor=False)
ax.set_yticklabels(column_labels, minor=False)
plt.show()

A oto wynik:

Matplotlib HeatMap

Jason Sundram
źródło