Utknąłem w czymś, co wydaje się stosunkowo łatwe. Kod, który przedstawiam poniżej, jest przykładem opartym na większym projekcie, nad którym pracuję. Nie widziałem powodu, aby publikować wszystkie szczegóły, więc zaakceptuj struktury danych, które przyniosłem.
Zasadniczo tworzę wykres słupkowy i mogę po prostu dowiedzieć się, jak dodać etykiety wartości do słupków (na środku słupka lub tuż nad nim). Oglądałem próbki w Internecie, ale bez powodzenia implementowałem je na własnym kodzie. Uważam, że rozwiązaniem jest albo „tekst”, albo „adnotacja”, ale ja: a) nie wiem, którego użyć (i ogólnie rzecz biorąc, nie wiem, kiedy użyć którego). b) nie widzą żadnego z nich, aby przedstawić etykiety wartości. Byłbym wdzięczny za pomoc, mój kod poniżej. Z góry dziękuję!
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)
źródło
Odpowiedzi:
Po pierwsze
freq_series.plot
zwraca oś, a nie cyfrę, więc aby moja odpowiedź była trochę bardziej przejrzysta, zmieniłem podany kod, aby odnosić się do niego,ax
a niefig
być bardziej spójnym z innymi przykładami kodu.Możesz pobrać listę prętów wyprodukowanych na działce od
ax.patches
członka. Następnie możesz użyć techniki przedstawionej w tymmatplotlib
przykładzie galerii, aby dodać etykiety przy użyciuax.text
metody.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')
Spowoduje to utworzenie opisanego wykresu, który wygląda następująco:
źródło
text
po prostu drukuje jakiś tekst na wykresie, będąc jednocześnieannotate
pomocnikiem, którego można użyć do łatwego dodania strzałki z tekstu wskazującej na określony punkt wykresu, do którego odnosi się tekst.W oparciu o funkcję wspomnianą w tej odpowiedzi na inne pytanie , znalazłem bardzo ogólnie stosowane rozwiązanie do umieszczania etykiet na wykresie słupkowym.
Inne rozwiązania niestety nie sprawdzają się w wielu przypadkach, ponieważ odstęp między etykietą a prętem jest albo podawany w jednostkach bezwzględnych prętów, albo jest skalowany o wysokość pręta . Pierwsza działa tylko dla wąskiego zakresu wartości, a druga zapewnia niespójne odstępy na jednym wykresie. Żadne z nich nie działa dobrze z osiami logarytmicznymi.
Proponowane przeze mnie rozwiązanie działa niezależnie od skali (tj. Dla małych i dużych liczb), a nawet poprawnie umieszcza etykiety dla wartości ujemnych i ze skalami logarytmicznymi, ponieważ wykorzystuje jednostkę wizualną
points
do przesunięć.Dodałem liczbę ujemną, aby pokazać prawidłowe umieszczenie etykiet w takim przypadku.
Wartość wysokości każdego słupka jest używana jako etykieta. Inne etykiety mogą być łatwo używane z fragmentem Simona
for rect, label in zip(rects, labels)
.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")
Edycja: wyodrębniłem odpowiednią funkcjonalność w funkcji, zgodnie z sugestią barnhillec .
Daje to następujący wynik:
A ze skalą logarytmiczną (i pewną korektą danych wejściowych, aby pokazać skalowanie logarytmiczne), oto wynik:
źródło
Opierając się na powyższej (świetnie!) Odpowiedzi, możemy również wykonać poziomy wykres słupkowy z kilkoma poprawkami:
# 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")
źródło
freq_series.plot(kind='barh', grid=True)
ax.set_xlim([0, 1.1*max_value])
Jeśli chcesz po prostu oznaczyć punkty danych powyżej słupka, możesz użyć plt.annotate ()
Mój kod:
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()
Określając poziome i pionowe wyrównanie
'center'
i'bottom'
odpowiednio, można uzyskać wyśrodkowane adnotacje.źródło
Jeśli chcesz dodać tylko punkty danych powyżej pasków, możesz to łatwo zrobić za pomocą:
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
źródło