[pandas] scatter plot

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Hallo,

habe seit langem Mal wieder mit Python etwas zu tun und versuche Daten aus einem Pandas-DataFrame in einem scatter-plot darzustellen. In gegebenem Beispiel

Code: Alles auswählen

import pandas as pd
df2 = pd.DataFrame(
    {"a" : [40700, 40813, 41012],
     "b" : [35.1, 32.8, 30.9],
     "c" : [1.2, 2.3, 1.9]},
    index = [1,2,3])
df2.plot(kind="scatter", x = "a", y = "b")
sind drei Datensätze (Reihen) vorhanden. Mein Problem dabei ist, daß die x-Achse einen Bereich (der die Werte aus Spalte a beinhalten) anzeigt. Gewünscht ist aber ein x-Achse mit drei diskreten Werten (40700, 40813 und 41012).
Habe schon versucht - bei meinem echten Daten - beim read_csv die Spalte a als dtype="{"a":str}" zu formatieren und/oder auch index_col angewandt. Leider läuft alles in Fehlermeldungen aus. Gibt es eine Möglichkeit gewünschtes Problem umzusetzen?

Grüße
Markus
einfachTobi
User
Beiträge: 510
Registriert: Mittwoch 13. November 2019, 08:38

Die Dokumentation von pandas.DataFrame.plot() zeigt, dass es einen Parameter `xticks` gibt, mit dem du diese Werte festlegen kannst.
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Hmm, das hilft ein wenig weiter. Allerdings sind die Abstände zwischen den x-Werten nicht äquidistant. Bei meinem realen Datensatz habe ich mehrere Gruppen in weitem Abstand und in den jeweiligen Gruppen kürzere Abstände, ähnlich wie hier:

Code: Alles auswählen

import pandas as pd
df2 = pd.DataFrame(
    {"a" : [40700, 40710, 40813, 41012, 41024],
     "b" : [35.1, 35.8, 32.8, 33.5, 30.9],
     "c" : [1.2, 2.3, 1.9, 1.2, 2.0]},
    index = [1,2,3,4,5])
df2.plot(kind="scatter", x = "a", y = "b", xticks=((40700, 40710, 40813, 41012, 41024)))
Benutzeravatar
__blackjack__
User
Beiträge: 13926
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich denke mal Du suchst den `FixedLocator`: https://matplotlib.org/3.2.1/gallery/ti ... ocators-py
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Hmm, so ganz komme ich mit den FixedLocator auch nicht wieter. Bin mir nicht sicher, ob ich es richtig anwende.

Code: Alles auswählen

import pandas as pd
import matplotlib.ticker as ticker
df2 = pd.DataFrame(
    {"a" : [40700, 40710, 40813, 41012, 41024],
     "b" : [35.1, 35.8, 32.8, 33.5, 30.9],
     "c" : [1.2, 2.3, 1.9, 1.2, 2.0]},
    index = [1,2,3,4,5])

df2.plot(kind="scatter", x = "a", y = "b", xticks=((40700, 40710, 40813, 41012, 41024)))

ax = plt.axes()
ax.xaxis.set_major_locator(ticker.FixedLocator([40700,40710,40813,41012,41024]))
df2.plot(kind="scatter", x = "a", y = "b")
Im ersten Bild werden die Ticks zwar richtig zugeordnet, haben aber eben den nicht-äquidistanten Abstand. Im zweiten Bild (FixedLocator angewendet) habe ich zwar konstante Abstände der Ticks aber eine fehlende Zuordnung zu den Datensätzen.
Benutzeravatar
__blackjack__
User
Beiträge: 13926
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mushroom: Das ist kein lauffähiges Beispiel. Davon abgesehen, dass `plt` aus dem Nichts kommt, fehlen die `plt.show()`-Aufrufe, damit man mal was sieht. Wenn man die einbaut, sieht man, dass das zu *drei* Plots führt, weil bei Fall zwei einmal mit `ax` ein Plot erstellt wird und dann noch mal einer durch das `df2.plot()`, und die beiden haben nichts miteinander zu tun.

Beim ersten der beiden Plots werden in Fall 2 nur die Ticks an die festen Positionen gesetzt, aber keine Daten geplottet. Die X-Achse scheint leer zu sein, weil ohne Daten der Bereich 0,0 bis 1,0 dargestellt wird, und die festen Tickpositionen *weit* ausserhalb davon liegen.

Wenn man dem ``df2.plot(…)`` das erzeugte `ax` übergibt, damit *da* drauf die Daten geplottet werden, bekomme ich aber das gleiche Bild wie im ersten Fall, wo ich mich dann jetzt frage was Du eigentlich haben willst wenn nicht das, oder ob Du da was anderes bekommst als ich.

Kann es sein das Spalte "a" gar keine X-Werte enthalten soll, sondern das das numerische Kategorien sind, also nur die Beschriftung der X-Ticks? Dann musst Du als X-Werte irgendwas ”künstliches”, schon gleichmässiges nehmen. Also einfach ein entsprechendes `range()`-Objekt oder in diesem Fall den Index von dem DataFrame. Und für die Beschriftung der Ticks dann Spalte "a". Die sollte man auch nicht kopiert in Quelltext stehen haben. Die Werte hat man doch bereits in `df2`.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Hallo blackjack,

sorry hatte den Codeteil aus mein Jupyter-Notebook rauskopiert und dabei die eine Importanweisung vergessen.

Tatsächlich trifft dein letzter Absatz die Problembeschreibung ganz gut. Wie kann ich denn df2.plot(...) mitteilen, daß er den index als x-Achse benutzen soll? Habe es mit ... x = df2.index probiert

Code: Alles auswählen

import pandas as pd
df2 = pd.DataFrame(
    {"a" : [40700, 40710, 40813, 41012, 41024],
     "b" : [35.1, 35.8, 32.8, 33.5, 30.9],
     "c" : [1.2, 2.3, 1.9, 1.2, 2.0]},
    index = [1,2,3,4,5])

df2.plot(kind="scatter", x = df2.index, y = "b", xticks=((40700, 40710, 40813, 41012, 41024)))
erhalte aber die Fehlermeldung

Code: Alles auswählen

KeyError: "None of [Int64Index([1, 2, 3, 4, 5], dtype='int64')] are in the [columns]"
Die xticks würde ich mir bei meinem realen Beispiel natürlich aus dem df2 holen, habe es hier nur quick and dirty gemacht.
Benutzeravatar
__blackjack__
User
Beiträge: 13926
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Nimm mal ``df2.index.values`` für `x`.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Liefert leider immer noch denselben Fehler.
Sirius3
User
Beiträge: 18219
Registriert: Sonntag 21. Oktober 2012, 17:20

Was hast Du denn nun versucht?
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Hatte den Hinweis wie folgt verstanden:

Code: Alles auswählen

import pandas as pd
df2 = pd.DataFrame(
    {"a" : [40700, 40710, 40813, 41012, 41024],
     "b" : [35.1, 35.8, 32.8, 33.5, 30.9],
     "c" : [1.2, 2.3, 1.9, 1.2, 2.0]},
    index = [1,2,3,4,5])

df2.plot(kind="scatter", x = df2.index.values, y = "b", xticks=((40700, 40710, 40813, 41012, 41024)))
also df2.index durch df2.index.values ersetzt. Will damit die x-Achseneinteilung durch den Index vornehmen und die Ticks dann durch Werte in Spalte "a" setzen.
Sirius3
User
Beiträge: 18219
Registriert: Sonntag 21. Oktober 2012, 17:20

Und das soll den selben Fehler liefern? Bei mir kommt da ein ganz anderer.
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Kurioserweise ja, obwohl print(df2.index.values) und print(df2.index) unterschiedliche Ergebnisse liefern. Komplettes traceback:

Code: Alles auswählen

KeyError                                  Traceback (most recent call last)
<ipython-input-3-7707213faada> in <module>
      7     index = [1,2,3,4,5])
      8 
----> 9 df2.plot(kind="scatter", x = df2.index.values, y = "b", xticks=((40700, 40710, 40813, 41012, 41024)))
     10 
     11 #ax = plt.axes()

C:\ProgramData\Anaconda3\lib\site-packages\pandas\plotting\_core.py in __call__(self, *args, **kwargs)
    736         if kind in self._dataframe_kinds:
    737             if isinstance(data, ABCDataFrame):
--> 738                 return plot_backend.plot(data, x=x, y=y, kind=kind, **kwargs)
    739             else:
    740                 raise ValueError(

C:\ProgramData\Anaconda3\lib\site-packages\pandas\plotting\_matplotlib\__init__.py in plot(data, kind, **kwargs)
     59                 ax = plt.gca()
     60             kwargs["ax"] = getattr(ax, "left_ax", ax)
---> 61     plot_obj = PLOT_CLASSES[kind](data, **kwargs)
     62     plot_obj.generate()
     63     plot_obj.draw()

C:\ProgramData\Anaconda3\lib\site-packages\pandas\plotting\_matplotlib\core.py in __init__(self, data, x, y, s, c, **kwargs)
    928             # the handling of this argument later
    929             s = 20
--> 930         super().__init__(data, x, y, s=s, **kwargs)
    931         if is_integer(c) and not self.data.columns.holds_integer():
    932             c = self.data.columns[c]

C:\ProgramData\Anaconda3\lib\site-packages\pandas\plotting\_matplotlib\core.py in __init__(self, data, x, y, **kwargs)
    867         if is_integer(y) and not self.data.columns.holds_integer():
    868             y = self.data.columns[y]
--> 869         if len(self.data[x]._get_numeric_data()) == 0:
    870             raise ValueError(self._kind + " requires x column to be numeric")
    871         if len(self.data[y]._get_numeric_data()) == 0:

C:\ProgramData\Anaconda3\lib\site-packages\pandas\core\frame.py in __getitem__(self, key)
   2984             if is_iterator(key):
   2985                 key = list(key)
-> 2986             indexer = self.loc._convert_to_indexer(key, axis=1, raise_missing=True)
   2987 
   2988         # take() does not accept boolean indexers

C:\ProgramData\Anaconda3\lib\site-packages\pandas\core\indexing.py in _convert_to_indexer(self, obj, axis, is_setter, raise_missing)
   1283                 # When setting, missing keys are not allowed, even with .loc:
   1284                 kwargs = {"raise_missing": True if is_setter else raise_missing}
-> 1285                 return self._get_listlike_indexer(obj, axis, **kwargs)[1]
   1286         else:
   1287             try:

C:\ProgramData\Anaconda3\lib\site-packages\pandas\core\indexing.py in _get_listlike_indexer(self, key, axis, raise_missing)
   1090 
   1091         self._validate_read_indexer(
-> 1092             keyarr, indexer, o._get_axis_number(axis), raise_missing=raise_missing
   1093         )
   1094         return keyarr, indexer

C:\ProgramData\Anaconda3\lib\site-packages\pandas\core\indexing.py in _validate_read_indexer(self, key, indexer, axis, raise_missing)
   1175                 raise KeyError(
   1176                     "None of [{key}] are in the [{axis}]".format(
-> 1177                         key=key, axis=self.obj._get_axis_name(axis)
   1178                     )
   1179                 )

KeyError: "None of [Int64Index([1, 2, 3, 4, 5], dtype='int64')] are in the [columns]"
Welchen Fehler erhälst du?
Sirius3
User
Beiträge: 18219
Registriert: Sonntag 21. Oktober 2012, 17:20

Genau, scatter erwartet die Namen der Spalten. Wenn du etwas anderes willst, musst du matplotlib direkt benutzen.
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Hallo,

habe es jetzt direkt in matplotlib gemacht. Der Vollständigkeit halber:

Code: Alles auswählen

import pandas as pd
import matplotlib.pyplot as plt

df2 = pd.DataFrame(
    {"a" : [40700, 40710, 40813, 41012, 41024],
     "b" : [35.1, 35.8, 32.8, 33.5, 30.9],
     "c" : [1.2, 2.3, 1.9, 1.2, 2.0]},
    index = [1,2,3,4,5])

plt.scatter(df2.index.values, df2.iloc[:,1])
plt.xticks(df2.index.values,df2.iloc[:,0])
plt.show()
Vielen Dank für eure Hilfe.
Sirius3
User
Beiträge: 18219
Registriert: Sonntag 21. Oktober 2012, 17:20

Statt iloc darfst du aber weiterhin über die Spaltennamen zugreifen.
Antworten