Matplotlib çubuk grafiğine değer etiketleri ekleme


95

Nispeten kolay olması gerektiğini düşünen bir şeye takıldım. Aşağıda getirdiğim kod, üzerinde çalıştığım daha büyük bir projeye dayalı bir örnek. Tüm ayrıntıları göndermek için bir neden görmedim, bu yüzden lütfen getirdiğim veri yapılarını olduğu gibi kabul edin.

Temel olarak, bir çubuk grafik oluşturuyorum ve çubuklara (çubuğun ortasında veya hemen üstünde) nasıl değer etiketleri ekleyeceğimi anlayabiliyorum. Web'deki örneklere baktım, ancak kendi koduma uygulama konusunda başarılı olamadım. Çözümün 'metin' veya 'açıklama' ile olduğuna inanıyorum, ancak ben: a) hangisini kullanacağımı bilmiyorum (ve genel olarak konuşursak, hangisini ne zaman kullanacağımı bulamadım). b) değer etiketlerini sunmak için ikisini de göremiyorum. Yardımınız için minnettarım, aşağıdaki kodum. Şimdiden teşekkürler!

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
pd.set_option('display.mpl_style', 'default') 
%matplotlib inline

# Bring some raw data.
frequencies = [6, 16, 75, 160, 244, 260, 145, 73, 16, 4, 1]

# In my original code I create a series and run on that, 
# so for consistency I create a series from the list.
freq_series = pd.Series.from_array(frequencies)

x_labels = [108300.0, 110540.0, 112780.0, 115020.0, 117260.0, 119500.0, 
            121740.0, 123980.0, 126220.0, 128460.0, 130700.0]

# Plot the figure.
plt.figure(figsize=(12, 8))
fig = freq_series.plot(kind='bar')
fig.set_title('Amount Frequency')
fig.set_xlabel('Amount ($)')
fig.set_ylabel('Frequency')
fig.set_xticklabels(x_labels)

2
Matplotlib bir demo vardır: matplotlib.org/examples/api/barchart_demo.html
Dan

Yanıtlar:


119

İlk olarak freq_series.plot, bir şekil değil bir eksen döndürür, bu yüzden cevabımı biraz daha açık hale getirmek için, verdiğiniz kodu diğer kod örnekleriyle daha tutarlı axolmaktan ziyade ona başvuracak şekilde değiştirdim fig.

Üyeden arsa içerisinde üretilen barların listesini alabilirsiniz ax.patches. Ardından , yöntemi kullanarak etiketleri eklemek için bu matplotlibgaleri örneğinde gösterilen tekniği kullanabilirsiniz ax.text.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Bring some raw data.
frequencies = [6, 16, 75, 160, 244, 260, 145, 73, 16, 4, 1]
# In my original code I create a series and run on that, 
# so for consistency I create a series from the list.
freq_series = pd.Series.from_array(frequencies)

x_labels = [108300.0, 110540.0, 112780.0, 115020.0, 117260.0, 119500.0,
            121740.0, 123980.0, 126220.0, 128460.0, 130700.0]

# Plot the figure.
plt.figure(figsize=(12, 8))
ax = freq_series.plot(kind='bar')
ax.set_title('Amount Frequency')
ax.set_xlabel('Amount ($)')
ax.set_ylabel('Frequency')
ax.set_xticklabels(x_labels)

rects = ax.patches

# Make some labels.
labels = ["label%d" % i for i in xrange(len(rects))]

for rect, label in zip(rects, labels):
    height = rect.get_height()
    ax.text(rect.get_x() + rect.get_width() / 2, height + 5, label,
            ha='center', va='bottom')

Bu, şuna benzeyen etiketli bir çizim oluşturur:

görüntü açıklamasını buraya girin


Merhaba Simon! İlk olarak, cevapladığınız için çok teşekkürler! İkincisi, sanırım net değildim - y değerini göstermek istedim. Az önce zip (,) içindeki etiketleri frekanslarla değiştirdim. Şimdi, incir Vs baltasına biraz daha ışık tutabilir misin? Kafam karıştı. İyi bir arama ifadesi / kaynağı, bir goog araması için biraz genel olduğu için harika olurdu. Çok takdir!
Optimesh

Bir şekil, bir veya daha fazla eksenden oluşan bir koleksiyondur, örneğin bu örnekte matplotlib.org/examples/statistics/… 4 farklı eksenden oluşan bir rakamdır.
Simon Gibbons

Tekrar teşekkürler. Lütfen açıklama ve metin arasındaki farkı anlamama yardım eder misiniz? Teşekkürler!
Optimesh

2
Her ikisi de bir çizim üzerine metin eklemek için kullanılabilir. textMetin annotatetarafından atıfta bulunulan arsa üzerinde belirli bir noktaya işaret eden metinden kolayca bir ok eklemek için kullanabileceğiniz bir yardımcı olurken , basitçe bir metni çizimin üzerine yazdırır .
Simon Gibbons

10
Güzel çözüm. Buradaki çözüme dayanan ve eksenin yüksekliğine göre ölçeklenen biraz daha sağlam bir sürüm veren bir blog yazısı yazdım, bu nedenle aynı kod, farklı eksen yüksekliklerine sahip farklı grafikler için çalışıyor: kompozisyon.al/blog/2015/
11/29

66

Bu yanıtta başka bir soruya verilen bir özelliğe dayanarak, bir çubuk grafiğe etiket yerleştirmek için çok genel olarak uygun bir çözüm buldum.

Etiket ve bar arasındaki mesafe, ya olduğu için diğer çözümler, ne yazık ki, bir çok durumda işe yaramaz bar mutlak birimler halinde verilecek olan ya da bar yüksekliği tarafından ölçekli . İlki yalnızca dar bir değerler aralığı için çalışır ve ikincisi, bir arsa içinde tutarsız boşluklar verir. Logaritmik eksenlerle iyi çalışmaz.

Önerdiğim çözüm ölçekten bağımsız çalışır (yani küçük ve büyük sayılar için) ve hatta negatif değerler için ve logaritmik ölçeklerle etiketleri doğru şekilde yerleştirir çünkü pointsofsetler için görsel birimi kullanır .

Böyle bir durumda etiketlerin doğru yerleşimini göstermek için negatif bir sayı ekledim.

Her çubuğun yüksekliğinin değeri bunun için bir etiket olarak kullanılır. Simon's for rect, label in zip(rects, labels)snippet ile diğer etiketler kolayca kullanılabilir .

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Bring some raw data.
frequencies = [6, -16, 75, 160, 244, 260, 145, 73, 16, 4, 1]

# In my original code I create a series and run on that,
# so for consistency I create a series from the list.
freq_series = pd.Series.from_array(frequencies)

x_labels = [108300.0, 110540.0, 112780.0, 115020.0, 117260.0, 119500.0,
            121740.0, 123980.0, 126220.0, 128460.0, 130700.0]

# Plot the figure.
plt.figure(figsize=(12, 8))
ax = freq_series.plot(kind='bar')
ax.set_title('Amount Frequency')
ax.set_xlabel('Amount ($)')
ax.set_ylabel('Frequency')
ax.set_xticklabels(x_labels)


def add_value_labels(ax, spacing=5):
    """Add labels to the end of each bar in a bar chart.

    Arguments:
        ax (matplotlib.axes.Axes): The matplotlib object containing the axes
            of the plot to annotate.
        spacing (int): The distance between the labels and the bars.
    """

    # For each bar: Place a label
    for rect in ax.patches:
        # Get X and Y placement of label from rect.
        y_value = rect.get_height()
        x_value = rect.get_x() + rect.get_width() / 2

        # Number of points between bar and label. Change to your liking.
        space = spacing
        # Vertical alignment for positive values
        va = 'bottom'

        # If value of bar is negative: Place label below bar
        if y_value < 0:
            # Invert space to place label below
            space *= -1
            # Vertically align label at top
            va = 'top'

        # Use Y value as label and format number with one decimal place
        label = "{:.1f}".format(y_value)

        # Create annotation
        ax.annotate(
            label,                      # Use `label` as label
            (x_value, y_value),         # Place label at end of the bar
            xytext=(0, space),          # Vertically shift label by `space`
            textcoords="offset points", # Interpret `xytext` as offset in points
            ha='center',                # Horizontally center label
            va=va)                      # Vertically align label differently for
                                        # positive and negative values.


# Call the function above. All the magic happens there.
add_value_labels(ax)

plt.savefig("image.png")

Düzenleme: barnhillec'in önerdiği gibi bir işlevdeki ilgili işlevselliği çıkardım .

Bu, aşağıdaki çıktıyı üretir:

Her çubuğa otomatik olarak yerleştirilmiş etiketlere sahip çubuk grafik

Ve logaritmik ölçekle (ve logaritmik ölçeklendirmeyi göstermek için giriş verilerinde bir miktar ayarlama), sonuç şudur:

Her çubuğa otomatik olarak yerleştirilmiş etiketlerle logaritmik ölçekli çubuk grafik


1
Harika cevap! Teşekkürler. Bu, yerleşik çubuk grafiğinde pandalarla kusursuz bir şekilde çalıştı.
m4p85r

1
Önerilen iyileştirme: plt.annotate yerine ax.annotate kullanın. Bu değişiklik, tüm rutinin bir eksen ekseninden geçirilen bir işlevde kapsüllenmesine izin verir ve bu daha sonra yararlı bir bağımsız çizim yardımcı program işlevine dönüştürülebilir.
barnhillec

@barnhillec, öneri için teşekkürler. Düzenlememde tam olarak bunu yaptım. Bunun şu anda yalnızca dikey çubuk grafiklerle çalıştığını ve diğer grafik türleriyle (belki histogramlarla) çalışmadığını unutmayın. Fonksiyonu daha genel hale getirmek, anlaşılmasını da zorlaştırır ve dolayısıyla burada bir yanıt için daha az uygun olur.
justfortherec

Bulduğum diğerlerinden çok sağlam cevap. Her satırı güzelce yorumlayarak açıklayın, tüm fikri özümsememe yardımcı olun.
code_conundrum

34

Yukarıdaki (harika!) Cevabı temel alarak, sadece birkaç ayarlamayla yatay bir çubuk grafiği de oluşturabiliriz:

# Bring some raw data.
frequencies = [6, -16, 75, 160, 244, 260, 145, 73, 16, 4, 1]

freq_series = pd.Series(frequencies)

y_labels = [108300.0, 110540.0, 112780.0, 115020.0, 117260.0, 119500.0, 
            121740.0, 123980.0, 126220.0, 128460.0, 130700.0]

# Plot the figure.
plt.figure(figsize=(12, 8))
ax = freq_series.plot(kind='barh')
ax.set_title('Amount Frequency')
ax.set_xlabel('Frequency')
ax.set_ylabel('Amount ($)')
ax.set_yticklabels(y_labels)
ax.set_xlim(-40, 300) # expand xlim to make labels easier to read

rects = ax.patches

# For each bar: Place a label
for rect in rects:
    # Get X and Y placement of label from rect.
    x_value = rect.get_width()
    y_value = rect.get_y() + rect.get_height() / 2

    # Number of points between bar and label. Change to your liking.
    space = 5
    # Vertical alignment for positive values
    ha = 'left'

    # If value of bar is negative: Place label left of bar
    if x_value < 0:
        # Invert space to place label to the left
        space *= -1
        # Horizontally align label at right
        ha = 'right'

    # Use X value as label and format number with one decimal place
    label = "{:.1f}".format(x_value)

    # Create annotation
    plt.annotate(
        label,                      # Use `label` as label
        (x_value, y_value),         # Place label at end of the bar
        xytext=(space, 0),          # Horizontally shift label by `space`
        textcoords="offset points", # Interpret `xytext` as offset in points
        va='center',                # Vertically center label
        ha=ha)                      # Horizontally align label differently for
                                    # positive and negative values.

plt.savefig("image.png")

ek açıklamalı yatay çubuk grafiği


1
freq_series.plot(kind='barh', grid=True)
Şebekenin

Grup çubuk grafikleriyle bile mükemmel çalışır. Teşekkürler.
Prabah

Yatay çubuk grafikle Güzelce Yapıldı!
code_conundrum

Benim için sayılar, çubuk grafiği çevreleyen kutuyla kesişiyor. Bunu önlemek için bir yol var mı?
bweber13

Kullanarak kendi sorunu çözüldüax.set_xlim([0, 1.1*max_value])
bweber13

14

Sadece çubuğun üstündeki veri noktalarını etiketlemek istiyorsanız, plt.annotate () kullanabilirsiniz.

Kodum:

import numpy as np
import matplotlib.pyplot as plt

n = [1,2,3,4,5,]
s = [i**2 for i in n]
line = plt.bar(n,s)
plt.xlabel('Number')
plt.ylabel("Square")

for i in range(len(s)):
    plt.annotate(str(s[i]), xy=(n[i],s[i]), ha='center', va='bottom')

plt.show()

Yatay ve dikey dizilim belirterek 'center've 'bottom'sırasıyla bir ortalanmış olabilir ek açıklamalar.

etiketli bir çubuk grafik


1
temiz ve basit
Ethan Yanjia Li

Etiketi tam ortasına nasıl yerleştirebileceğimizi ekleyebilir misiniz?
x89

@ x89 Ortalama yapan metnin yatay ve dikey hizalamasını belirleyebilirsiniz. - Bununla geliştirmek için cevabı düzenledim.
Simon Gibbons

0

Yalnızca çubukların üzerine Veri Noktaları eklemek istiyorsanız, aşağıdakilerle kolayca yapabilirsiniz:

 for i in range(len(frequencies)): # your number of bars
    plt.text(x = x_values[i]-0.25, #takes your x values as horizontal positioning argument 
    y = y_values[i]+1, #takes your y values as vertical positioning argument 
    s = data_labels[i], # the labels you want to add to the data
    size = 9) # font size of datalabels
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.