Подтвердить что ты не робот

Matplotlib: рисование линий между точками, игнорирующими отсутствующие данные

У меня есть набор данных, которые я хочу построить в виде линейного графика. Для каждой серии некоторые данные отсутствуют (но разные для каждой серии). В настоящее время matplotlib не рисует строки, пропускающие отсутствующие данные: например

import matplotlib.pyplot as plt

xs = range(8)
series1 = [1, 3, 3, None, None, 5, 8, 9]
series2 = [2, None, 5, None, 4, None, 3, 2]

plt.plot(xs, series1, linestyle='-', marker='o')
plt.plot(xs, series2, linestyle='-', marker='o')

plt.show()

приводит к графику с зазорами в линиях. Как я могу сказать matplotlib, чтобы нарисовать линии через промежутки? (Я бы предпочел не интерполировать данные).

4b9b3361

Ответ 1

Вы можете маскировать значения NaN таким образом:

import numpy as np
import matplotlib.pyplot as plt

xs = np.arange(8)
series1 = np.array([1, 3, 3, None, None, 5, 8, 9]).astype(np.double)
s1mask = np.isfinite(series1)
series2 = np.array([2, None, 5, None, 4, None, 3, 2]).astype(np.double)
s2mask = np.isfinite(series2)

plt.plot(xs[s1mask], series1[s1mask], linestyle='-', marker='o')
plt.plot(xs[s2mask], series2[s2mask], linestyle='-', marker='o')

plt.show()

Это приводит к

Plot

Ответ 2

Qouting @Rutger Kassies (ссылка):

Matplotlib только рисует линию между последовательными (действительными) точками данных, и оставляет зазор при значениях NaN.

Решение, если вы используете Pandas,:

#pd.Series 
s.dropna().plot() #masking (as @Thorsten Kranz suggestion)

#pd.DataFrame
df['a_col_ffill'] = df['a_col'].ffill(method='ffill')
df['b_col_ffill'] = df['b_col'].ffill(method='ffill')  # changed from a to b
df[['a_col_ffill','b_col_ffill']].plot()

Ответ 3

Без интерполяции вам нужно удалить None из данных. Это также означает, что вам нужно будет удалить значения X, соответствующие None в серии. Здесь (уродливый) один вкладыш для этого:

  x1Clean,series1Clean = zip(* filter( lambda x: x[1] is not None , zip(xs,series1) ))

Функция лямбда возвращает False для значений None, фильтруя пары x, series из списка, затем повторно заносит данные обратно в исходную форму.

Ответ 4

Для чего, возможно, стоит, после некоторых проб и ошибок, я хотел бы добавить одно разъяснение к решению Thorsten. Надеемся, что сэкономить время для пользователей, которые искали в другом месте, пробовав этот подход.

Мне не удалось добиться успеха с одинаковой проблемой при использовании

from pyplot import *

и попытка построить с помощью

plot(abscissa[mask],ordinate[mask])

Казалось, что требуется использовать import matplotlib.pyplot as plt, чтобы получить правильную обработку NaNs, хотя я не могу сказать, почему.

Ответ 5

Решение с пандами:

import matplotlib.pyplot as plt
import pandas as pd

def splitSerToArr(ser):
    return [ser.index, ser.as_matrix()]


xs = range(8)
series1 = [1, 3, 3, None, None, 5, 8, 9]
series2 = [2, None, 5, None, 4, None, 3, 2]

s1 = pd.Series(series1, index=xs)
s2 = pd.Series(series2, index=xs)

plt.plot( *splitSerToArr(s1.dropna()), linestyle='-', marker='o')
plt.plot( *splitSerToArr(s2.dropna()), linestyle='-', marker='o')

plt.show()

Функция splitSerToArr очень удобна при построении в Pandas. Это результат: enter image description here

Ответ 6

Возможно, я пропустил этот момент, но я верю, что Pandas теперь делает это автоматически. Приведенный ниже пример немного связан и требует доступа в Интернет, но линия для Китая имеет много пробелов в первые годы, следовательно, прямые сегменты.

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

# read data from Maddison project 
url = 'http://www.ggdc.net/maddison/maddison-project/data/mpd_2013-01.xlsx'
mpd = pd.read_excel(url, skiprows=2, index_col=0, na_values=[' ']) 
mpd.columns = map(str.rstrip, mpd.columns)

# select countries 
countries = ['England/GB/UK', 'USA', 'Japan', 'China', 'India', 'Argentina']
mpd = mpd[countries].dropna()
mpd = mpd.rename(columns={'England/GB/UK': 'UK'})
mpd = np.log(mpd)/np.log(2)  # convert to log2 

# plots
ax = mpd.plot(lw=2)
ax.set_title('GDP per person', fontsize=14, loc='left')
ax.set_ylabel('GDP Per Capita (1990 USD, log2 scale)')
ax.legend(loc='upper left', fontsize=10, handlelength=2, labelspacing=0.15)
fig = ax.get_figure()
fig.show()