Страницы

Поиск по вопросам

Показаны сообщения с ярлыком matplotlib. Показать все сообщения
Показаны сообщения с ярлыком matplotlib. Показать все сообщения

воскресенье, 29 марта 2020 г.

Как построить график разрывной функции?

#python #математика #matplotlib #графики


Я построил график функции путем создания двух прямых, не включающих в себя точку
разрыва: 

xList1 = np.arange(a, -5-h, h)
xList2 = np.arange(-5+h, b, h)

lineF1 = plt.plot(xList1, f(xList1), color='b', linewidth=2.0)
lineF2 = plt.plot(xList2, f(xList2), color='b', linewidth=2.0)


Для удобства, хотелось бы объединить xList1 и xList2, но в таком случае на графике
не образуется разрыв, а точки, которые должны были быть на границах разрыва, соединяются.
Есть ли способ избежать этого и построить график правильно?
    


Ответы

Ответ 1



для того чтобы получить разрыв достаточно заменить значения по X и Y - NaN (Not a Number): In [150]: x = np.linspace(-7, 7, 100) In [151]: x[(x>-1) & (x<2)] = np.nan In [152]: plt.plot(x, np.sin(x)) Out[152]: []

суббота, 21 марта 2020 г.

Matplotlib.pyplot.subplots() не отображает графики seaborn на разных сетках

#python #matplotlib #seaborn


Есть непрерывная переменная df_train_tr['TransactionAmt'], хочу построить плотность
распределения переменной в изначальном формате и после применения функции log.

fig, ax = plt.subplots(1, 2, figsize=(11, 5), sharey=True)
plt.style.use('classic')
fig.patch.set_facecolor('xkcd:white')

ax[0]= sns.distplot(df_train_tr[df_train_tr['TransactionAmt'] <= 1000]['TransactionAmt'])
ax[0].set_xlabel('TransactionAmt', fontsize=16)
ax[0].set_title('Transaction Value distibution')
ax[0].set_ylabel('Probability', fontsize=16)
ax[0].axis(xmin=0)

ax[1] = sns.distplot(np.log(df_train_tr[df_train_tr['TransactionAmt'] <= 1000]['TransactionAmt']))
ax[1].set_xlabel('TransactionAmt', fontsize=16)
ax[1].set_title('Transaction Value distibution')
ax[1].set_ylabel('Probability', fontsize=16)
ax[1].axis(xmin=0)

plt.show()



 Данный код рисует все на одном графике и, самое интересное, переменная ax состоит
из двух одинаковых объектов

[,
 ],
          dtype=object)


Хотя до исполнения строчек с ax[0] и ax[1] объекты разные

[
 ]


Переписал, используя вместо sns.distplot() plt.hist()

ax[0].hist(df_train_tr[df_train_tr['TransactionAmt'] <= 1000]['TransactionAmt'])
#ax[0]= sns.distplot(df_train_tr[df_train_tr['TransactionAmt'] <= 1000]['TransactionAmt'])
ax[0].set_xlabel('TransactionAmt', fontsize=16)
ax[0].set_title('Transaction Value distibution')
ax[0].set_ylabel('Probability', fontsize=16)
ax[0].axis(xmin=0)

ax[1].hist(np.log(df_train_tr[df_train_tr['TransactionAmt'] <= 1000]['TransactionAmt']))
#ax[1] = sns.distplot(np.log(df_train_tr[df_train_tr['TransactionAmt'] <= 1000]['TransactionAmt']))
ax[1].set_xlabel('TransactionAmt', fontsize=16)
ax[1].set_title('Transaction Value distibution')
ax[1].set_ylabel('Probability', fontsize=16)
ax[1].axis(xmin=0)

plt.show()


И получил то, что хотел (y_axis - sic!)

Можно ли переписать код, чтобы получить второй вариант, но используя Seaborn?


    


Ответы

Ответ 1



Укажите явно axis, с которой работаете: import matplotlib.pyplot as plt import seaborn as sns import numpy as np fig, axs = plt.subplots(1, 2, figsize=(11, 5), sharey=True) plt.style.use('classic') fig.patch.set_facecolor('xkcd:white') x = np.random.randn(100) ax=axs[0] ax= sns.distplot(x, ax=ax) # <-- здесь ax.set_xlabel('TransactionAmt', fontsize=16) ax.set_title('Transaction Value distibution') ax.set_ylabel('Probability', fontsize=16) ax.axis(xmin=0) ax=axs[1] ax = sns.distplot(np.log(abs(x)), ax=ax, hist=False) # здесь я еще и гистограмму # отключил. для наглядности ax.set_xlabel('TransactionAmt', fontsize=16) ax.set_title('Transaction Value distibution') ax.set_ylabel('Probability', fontsize=16) ax.axis(xmin=0) plt.show()

среда, 26 февраля 2020 г.

Как получить данные колонки индексов Pandas DataFrame?

#python #pandas #dataframe #matplotlib


Считываю данные:

data = pd.read_csv('data.csv',';', index_col=['month'], parse_dates=['month'], dayfirst=True)


Смотрю на них:

print data.shape
print data.columns
data.head()


Вывод:

(168, 1)
Index([u'milk'], dtype='object')


   month    milk
1962-01-01  589
1962-02-01  561
1962-03-01  640
1962-04-01  656
1962-05-01  727


В какой-то момент мне необходимо построить график количества во времени. Каким образом
получить данные колонки индексов (month)? Напомню, что мы ее указали индексом и в данных
у нас только одна колонка с количеством. Конечно, можно отобразить как data.plot(),
но мне хотелось бы сделать это вызвав plot у matplotlib.

Дополнительно вопрос о лучших практиках: в каких случаях обоснованно / следует использовать
данные в качестве индекса?
    


Ответы

Ответ 1



Судя по выводу data.head(), вы либо не указали параметр index_col=['month'] либо он отработал не так как ожидалось и столбец month оказался обычным столбцом, вместо того чтобы стать индексом. Чтобы получить значения индекса DataFrame'а, достаточно воспользоваться атрибутом DataFrame.index. Пример: In [129]: df = pd.DataFrame({'val':np.random.rand(100)}, index=np.arange(100, 200)) In [130]: df Out[130]: val 100 0.924396 101 0.960312 102 0.728403 103 0.435330 104 0.599777 .. ... 195 0.055345 196 0.560087 197 0.049506 198 0.819265 199 0.231141 [100 rows x 1 columns] In [131]: plt.plot(df.index, df['val']) Out[131]: [] UPDATE: если на оси X - дата/время, то проще воспользоваться Pandas для визуализации: In [154]: df = pd.read_csv(r'C:\download\data.csv', sep=';', parse_dates=True, index_col=0) In [155]: df Out[155]: milk month 1962-01-01 589 1962-02-01 561 1962-03-01 640 1962-04-01 656 1962-05-01 727 In [156]: df.plot(y='milk') Out[156]: Значения индекса DataFrame: In [157]: df.index Out[157]: DatetimeIndex(['1962-01-01', '1962-02-01', '1962-03-01', '1962-04-01', '1962-05-01'], dtype='datetime64[ns]', name='month', freq=None)

Как отрегулировать масштаб heatmap матрицы корреляций?

#python #pandas #dataframe #matplotlib #seaborn


id  Temperature Humidity    Light   CO2 HumidityRatio   Occupancy
date                            
2015-02-04 17:51:00 1   2.518315    0.278509    1.573666    0.364926    1.091690    1.0
2015-02-04 17:51:59 2   2.488814    0.277696    1.591637    0.341860    1.080489    1.0
2015-02-04 17:53:00 3   2.488814    0.273628    1.573666    0.340269    1.075822    1.0
2015-02-04 17:54:00 4   2.488814    0.265492    1.573666    0.323567    1.066489    1.0
2015-02-04 17:55:00 5   2.439646    0.265492    1.573666



plt.figure(figsize=(7,6))
corr = df.drop('id', axis=1).corr()
sns.heatmap(corr, cmap=sns.color_palette("RdBu_r", 1000), vmin=-1, center=0, annot=True)




Как видно на графике верх и низ срезаны. Как полностью втиснуть квадратик в график?
https://drive.google.com/file/d/1YzGDML_wBZSJTGip3yZ1uk6HTTCSg_nD/view?usp=sharing
Как получены данные:

import pandas as pd
df = pd.read_csv('datatraining.txt', sep=r',', engine='python', header=None, names
= ['id', 'date','Temperature','Humidity','Light','CO2','HumidityRatio','Occupancy'])
df = df.drop([0])
df.index = pd.to_datetime(df.date)
df.drop('date', axis=1, inplace=True)
df = df.apply(pd.to_numeric)
def scale(df):
    return (df - df.mean()) / df.std()
df.Temperature = scale(df.Temperature)
df.Humidity = scale(df.Humidity)
df.Light = scale(df.Light)
df.CO2 = scale(df.CO2)
df.HumidityRatio = scale(df.HumidityRatio)

    


Ответы

Ответ 1



Вместо sns.heatmap(corr, cmap=sns.color_palette("RdBu_r", 1000), vmin=-1, center=0, annot=True) Сделайте так: ax = sns.heatmap(corr, cmap=sns.color_palette("RdBu_r", 1000), vmin=-1, center=0, annot=True) i, k = ax.get_ylim() ax.set_ylim(i+0.5, k-0.5) #устанавливаем границы вручную

Ответ 2



Используйте специально предназначенную для этого функцию plt.tight_layout() в конце: sns.heatmap(corr, cmap=sns.color_palette("RdBu_r", 1000), vmin=-1, center=0, annot=True) plt.tight_layout()

четверг, 13 февраля 2020 г.

Как в одной картинке вывести несколько графиков по классам?

#python #pandas #dataframe #matplotlib #seaborn


Загружаю данные из датасета iris:

from sklearn import datasets
data = datasets.load_iris(return_X_y=False)
X = data.data
y = data.target
names = data.target_names


Датафрейм со всеми данными и целевой переменной в y:

dfy = pd.DataFrame(y)
df = pd.DataFrame(X)
dfy = dfy.rename(columns={0: 'y'})
df_ = pd.concat([df,dfy], axis = 1)

sns.pairplot(df_, hue = 'y')


Несколько боксплотов в одной картинке без классов выводится:

plt.figure(figsize=(14,7))
sns.boxplot(data=df_.drop(['y'], axis=1), palette=sns.color_palette('Greens'))
plt.show()


А несколько бохплотов с классами из столбца y смогла вывести только так:

for i in range(4):
        sns.boxplot(data=df_, x ='y', y = i, hue='y')
        plt.show()


Как вывести несколько бохплотов с классами в одной картинке?
    


Ответы

Ответ 1



df = (pd.DataFrame( data.data, columns=data.feature_names) .assign(target=data.target)) d = (df.set_index("target") .stack() .reset_index(name="val") .rename(columns={"level_1":"feature"})) sns.boxplot(x="feature", y="val", hue="target", data=d) или так: sns.boxplot(x="feature", y="val", data=d)

вторник, 28 января 2020 г.

как обновлять график matplotlib в tkinter раз в 5 минут данными из базы данных?

#python #tkinter #matplotlib


Что нужно: Каждые 5 секунд, считываются координаты из БД и отображаются на графике
matplotlib в окне tkinter.

Что сделано: рисуются только первый набор данных и не рисуются второй набор

ВОПРОС: как исправить чтобы рисовался сначала первый набор данных , а через 5 секунд
второй?

Вот обновленный минимальный рабочий код:

from threading import Thread
from queue import Empty, Queue
import time
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg#, NavigationToolbar2TkAgg
from matplotlib.figure import Figure

class tkChartGUI(tk.Frame):       

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)   
        self.parent = parent
        self.initUI()

    def get_latest_data(self, dataid):
        x_array=[]
        y_array=[]
        if (dataid == 1):
            x_array=[1,2,3,4,5,6,7,8,9,10,11,12,13,14];
            y_array=[0.5,0.7,0.3,1.0,0.6,0.9,0.5,0.2,0.1,0.5,0.33,0.55,0.3,0.6] 
        if (dataid == 2):
            x_array=[1,2,3,4,5,6,7,8,9,10,11,12,13,14];
            y_array=[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.1,1.2,1.3] 
        return (x_array, y_array)

    def initUI(self):
        self.parent.title("Simple chart")
        self.parent.geometry("800x600+300+100")

        result_queue = Queue()
        Thread(target=self.get_latest_data, args=[result_queue], daemon=True).start()

        x_array, y_array = self.get_latest_data(1)

        f = Figure(figsize=(5, 3), dpi=150)        
        a = f.add_subplot(111)
        a.set_xlabel("Values_X")
        a.set_ylabel("Values_Y")
        a.yaxis.grid(True, which='major')
        a.xaxis.grid(True, which='major')
        a.plot(x_array, y_array)

        canvas = FigureCanvasTkAgg(f, master=self.parent)
        canvas.show()                        
        canvas.get_tk_widget().grid(row=0,column=0)

        def display_result(a, q):
            x_array = []
            y_array = []
            try:                
                x_array = q.get(block=False) # get data
                y_array = q.get(block=False)                           
                a.plot(x_array, y_array)
                canvas.draw() 
            except Empty:
                #a.clear()                            
                timeout_millis = round(100 - (5000 * time.time()) % 100)
                self.parent.after(timeout_millis, display_result, a, q)

        def get_result(q):
            x_array, y_array = self.get_latest_data(2)
            q.put(x_array) # put data in FIFO queue x coords array
            q.put(y_array) # put data in FIFO queue y coords array

        display_result(a, result_queue)

    def onExit(self):
        self.quit()

def main():
    root = tk.Tk()
    my_gui = tkChartGUI(root) 
    root.mainloop()

if __name__ == '__main__':
    main()

    


Ответы

Ответ 1



Чтобы постоянно не считывать данные из базы данных (polling), можно определить trigger, чтобы вызвать функцию (callback), когда в БД интересное событие произойдёт. К примеру, когда в нужную таблицу новое значение добавляется (Launch a Python Script from a sqlite3 Trigger): CREATE TRIGGER tt AFTER INSERT ON t BEGIN SELECT got_y(NEW.y); END Обновления графика происходят в got_y() обратном вызове, определённом в make_callback() ниже. Код похож на ответ c loop(), определённой с помощью .after(), с той разницей, что здесь функция вызывается, когда нужные данные готовы, вместо того чтобы периодически непрерывно новые данные запрашивать без блокировки: #!/usr/bin/env python3 import datetime as DT import sqlite3 import random import threading import time import tkinter as tk from collections import deque import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.dates import date2num def generate_data(db): # generate dummy data for the example while True: time.sleep(.1) db.execute('insert into t values(?)', (random.randrange(100),)) def make_callback(root): # plot something # see @MaxU answer https://ru.stackoverflow.com/q/801923/23044 n = 300 def get_t(): return date2num(DT.datetime.now()) xx = deque([get_t()], maxlen=n) yy = deque([0], maxlen=n) fig, ax = plt.subplots() ax.set_ylim(0, 100) line, = ax.plot_date(xx, yy, marker='') format_time = '{:%Y-%m-%d %H:%M:%S}'.format time_text = ax.text(0.5, 0.9, '', transform=ax.transAxes) # add to GUI canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea. canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) def got_y(y): # update plot xx.append(get_t()) yy.append(int(y)) line.set_data(xx, yy) ax.fill_between(xx, 0, yy, color='lightgrey') time_text.set_text(format_time(DT.datetime.now())) ax.relim() # update axes limits ax.autoscale_view(scaley=False) canvas.draw() return got_y def main(): root = tk.Tk() # a dummy db for the example db = sqlite3.connect(':memory:', check_same_thread=False) db.execute('create table t(y)') db.create_function('got_y', 1, make_callback(root)) db.execute('CREATE TRIGGER tt AFTER INSERT ON t BEGIN SELECT got_y(NEW.y); END') threading.Thread(target=generate_data, args=[db], daemon=True).start() root.mainloop() if __name__ == '__main__': main() Здесь db.create_function() определяет какая Питон-функция используется в качестве got_y(y) обратного вызова. В примере, callback вызывается в дочернем потоке, что не всегда желаемо. Поток с generate_data() используется только для примера, фактически данные могут вставляться в базу данных из другого процесса или других машин. How to receive automatic notifications about changes in tables? Для сравнения вот вариант с polling, где база данных постоянно опрашивается в фоновом потоке, используя простой цикл (про достоинства и недостатки цикла подробно описано в Как правильно сделать временный цикл?) def poll_db(interval=5): while True: time.sleep(interval - time.time() % interval) # avoid drift emit(get_y_from_db()) здесь get_y_from_db() делает запрос к базе данных, а emit() генерирует событие для GUI: #!/usr/bin/env python3 import random import threading import time import tkinter as tk def get_y_from_db(): # generate dummy data time.sleep(random.random()) # emulate blocking function return random.randrange(100) def poll_db(emit, interval=5): while True: time.sleep(interval - time.time() % interval) # avoid drift emit(get_y_from_db()) root = tk.Tk() root.bind('<>', lambda e, f=make_callback(root): f(e.y)) # subscribe threading.Thread(target=poll_db, args=[lambda y: root.event_generate('<>', when='tail', y=y)], daemon=True).start() root.mainloop() Пример кода (где этот метод также используется): вывод процесса показывается в Tkinter GUI с помощью root.event_generate() Не на всех реализациях можно root.event_generate() в фоновом потоке вызвать. В таких случаях можно использовать queue, чтобы данные между потоками передавать: #!/usr/bin/env python3 import datetime as DT import random import threading import time import tkinter as tk from collections import deque from queue import Empty, Queue from time import time as timer import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.dates import date2num def get_y_from_db(): # generate dummy data time.sleep(random.random()) # emulate blocking function return random.randrange(100) def poll_db(emit, interval=1): # NOTE: interval is independant from the delay while True: time.sleep(interval) emit(get_y_from_db()) def start_polling_loop(root, q, delay): # plot something # see @MaxU answer https://ru.stackoverflow.com/q/801923/23044 n = 600 def get_t(): return date2num(DT.datetime.now()) xx = deque([get_t()], maxlen=n) yy = deque([0], maxlen=n) fig, ax = plt.subplots() ax.set_ylim(0, 100) line, = ax.plot_date(xx, yy, marker='') format_time = '{:%Y-%m-%d %H:%M:%S}'.format time_text = ax.text(0.5, 0.9, '', transform=ax.transAxes) # add to GUI canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea. canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) # update in a loop def loop(): timeout_millis = round(delay - (1000 * timer()) % delay) root.after(timeout_millis, loop) # avoid drift try: y = q.get(block=False) except Empty: return # no new data # update plot xx.append(get_t()) yy.append(y) line.set_data(xx, yy) ax.fill_between(xx, 0, yy, color='lightgrey') time_text.set_text(format_time(DT.datetime.now())) ax.relim() # update axes limits ax.autoscale_view(scaley=False) canvas.draw() root.after_idle(loop) # start root = tk.Tk() q = Queue() threading.Thread(target=poll_db, args=[q.put], daemon=True).start() start_polling_loop(root, q, delay=40) root.mainloop() Упрощённый вариант (с одним обновлением данных из потока), см. в Как сделать постоянное обновление окна Tkinter? Как избежать подвисания на время ожидания ответа от сервера. Ещё пример кода (где этот метод используется): вывод процесса показывается в Tkinter GUI, используя widget.after(), q.get()/q.put().

Ответ 2



Чтобы в tkinter периодически действие выполнять, можно его запускать с помощью after tcl команды. См. Обновление Label из цикла в tkinter. На каждой итерации, обновляйте желаемые элементы, к примеру, как показано в Python (jupyter) анимированный график и вызывайте canvas.draw(): #!/usr/bin/env python3 import datetime as DT import tkinter as tk from collections import deque from time import time as timer import matplotlib.pyplot as plt import psutil from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.dates import date2num def start_loop(root, delay): # plot something # see @MaxU answer https://ru.stackoverflow.com/q/801923/23044 n = 600 def get_t(): return date2num(DT.datetime.now()) xx = deque([get_t()], maxlen=n) yy = deque([0], maxlen=n) fig, ax = plt.subplots() ax.set_ylim(0, 100) line, = ax.plot_date(xx, yy, marker='') format_time = '{:%Y-%m-%d %H:%M:%S}'.format time_text = ax.text(0.5, 0.9, '', transform=ax.transAxes) # add to GUI canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea. canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) # update in a loop def loop(): timeout_millis = round(delay - (1000 * timer()) % delay) root.after(timeout_millis, loop) # avoid drift # update plot # cpu sine https://stackoverflow.com/q/551494/4279 xx.append(get_t()) yy.append(psutil.cpu_percent()) line.set_data(xx, yy) ax.fill_between(xx, 0, yy, color='lightgrey') time_text.set_text(format_time(DT.datetime.now())) ax.relim() # update axes limits ax.autoscale_view(scaley=False) canvas.draw() root.after_idle(loop) # start def quit(): root.quit() root.destroy() # https://matplotlib.org/gallery/user_interfaces/embedding_in_tk_sgskip.html root = tk.Tk() start_loop(root, 100) root.protocol("WM_DELETE_WINDOW", quit) root.eval('tk::PlaceWindow %s center' % root.winfo_pathname(root.winfo_id())) root.mainloop() Сутью решения является loop() функция здесь.

среда, 22 января 2020 г.

Python matplotlib Построение гиперболы

#python #график #matplotlib


Добрый день! Хочу при помощи matplotlib построить, например, гиперболу. Есть следующий код:

import matplotlib.pyplot as plt
import pylab, math
xmin = -200
xmax = 200
dx = 0.1

xlist = [float(x) for x in range(xmin,xmax)]
ylist = [float(1/x) for x in range(xmin,xmax)]


ax = plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))
plt.grid(True)
plt.title('График функции ')
pylab.plot(xlist,ylist)
pylab.savefig('123.png')


Пример работы на картинке
Как видно, между двумя ветвями функции есть отрезок. Я знаю, что это связанно с тем,
что есть значение крайних точек ветвей, а плот их соединяет. Но это мне не нужно, мне
нужно получить нормальный график. Если бы этот график был всегда гиперболой, было бы
нормально. Но этим графиком может быть любой график с разрывом. Как избежать этого
отрезка? Видел способы, но там использовались дополнительные пакеты(numpy), я не хочу
тянуть их в этот код
[]
    


Ответы

Ответ 1



Для начала повторюсь, что matplotlib уже внутри себя использует numpy, так что избежать его не выйдет. Тем не менее, вот первая версия без numpy в основном коде: import matplotlib.pyplot as plt import decimal # Это замена np.arange def arange(start, stop, step): while start < stop: yield start start += decimal.Decimal(step) hyperbola = lambda x: 1 / x def asymptote_checker(argument, function): try: function(argument) return True except ZeroDivisionError: return False xmin = -20 xmax = 20 dx = 0.1 xlist = [round(x, 4) for x in arange(xmin, xmax, dx)] ylist = [hyperbola(x) if asymptote_checker(x, hyperbola) else float('nan') for x in xlist] plt.plot(xlist, ylist) plt.show() С numpy выйдет покороче: import matplotlib.pyplot as plt import decimal import numpy as np xmin = -20 xmax = 20 dx = 0.1 xlist = np.around(np.arange(xmin, xmax, dx), decimals=4) ylist = 1 / xlist plt.plot(xlist, ylist) plt.show() Пара общих важных моментов для обеих версий: числа в xlist вместо нуля могут внезапно превратиться в мусор типа 7.0485345e-13 или типа того, что выльется в огромное значение по оси Oy. Второй момент - matplotlib игнорирует (и не соединяет точки) числа типа +inf, -inf, NaN - их можно пометить руками, доверить это numpy или использовать numpy masked array.

воскресенье, 12 января 2020 г.

Как рисовать в Matplotlib в комплексных координатах?

#python #matplotlib


Требуется изобразить вот такую функцию:

w(omega) = 1/(1 + 1j*omega)


в комплексных координатах. Как?
    


Ответы

Ответ 1



Вот такой вариант вам не подойдет? import numpy as np import matplotlib.pyplot as plt omega = np.linspace(0, 5, 100) w = 1 / (1 + 1j*omega) plt.figure(1) plt.scatter(w.real, w.imag) plt.show()

суббота, 11 января 2020 г.

Как исправить смещение на графике?

#python #pandas #dataframe #matplotlib


На основе данных Таблицы был построен  упорядоченный график ящиков с усами . Однако,
при добавлении функции средней (строка 11), график смещается, почему? 



import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_excel(r'C:\Users\cinem\Desktop\нир\3-6.xlsx')
r = df.pivot_table(columns='town_name', index='cld_year',
                   values='относительная заболеваемость на 1000 чел.',
                   aggfunc='mean', fill_value=0)
aq = r.median().sort_values().index
ae = r.mean().sort_values().index
r[aq].plot.box(rot=90, figsize=(16,10))
r[ae].mean().plot(kind='line', color='orange', rot=90)
plt.ylabel('Относительная заболеваемость на 1000 чел.')
plt.xlabel('город')
plt.title('Относительная заболеваемость на 1000 человек в городах РФ')
plt.tight_layout()
plt.show()






    


Ответы

Ответ 1



r = df.pivot_table(columns='town_name', index='cld_year', values='относительная заболеваемость', aggfunc='mean', fill_value=0) idx = r.mean().sort_values().index r = r[idx] fig, ax = plt.subplots(figsize=(16,10)) r.plot.box(rot=90, ax=ax) ax2 = ax.twinx() ax2.plot(ax.get_xticks(), r.mean(), color='orange') ax2.plot(ax.get_xticks(), r.median(), color='green') ax2.set_ylim(ax.get_ylim()) plt.legend(['mean','median'])

пятница, 10 января 2020 г.

Как соединить точки в matplotlib

#python #python_3x #matplotlib #numpy


Всем привет. Перед мной стоит задача соединить точки, но как бы я не пытался ничего
не выходит либо точки, либо пустой график.

Как  можно соединить точки, так чтобы образовалась сеть?

И вопрос не в тему, почему на втором графике не задается сетка? Задается только если
явно в цикле писать.



import matplotlib.pyplot as plt
import numpy as np


def polar(n,m,L=(0,0),scale=1):
    #n - кол-во кругов, m - кол-во точек, L - центр, scale - масштаб
    dr=1/n#Расстояние
    df=(2*np.pi)/m#Поворот
    for i in range(n+1):
        for k in range(m+1):
            z=i*dr*np.cos(k*df)+1j*i*dr*np.sin(k*df)
            w=(z-1)**2
            plt.subplot(122)
            plt.plot(w.real*scale,w.imag*scale,'r.')
            circle=i*dr*np.cos(k*df)+1j*i*dr*np.sin(k*df)
            plt.subplot(121)
            plt.plot(circle.real*scale,circle.imag*scale,'r.')
    plt.axis('equal')
    plt.ylabel('Imaginary')
    plt.xlabel('Real')
    plt.grid(True)
    plt.show()


polar(25,25)

    


Ответы

Ответ 1



Если задача - соединить точки, принадлежащие концентрическим окружностям, то можно так сделать: def polar(n,m,L=(0,0),scale=1): #n - кол-во кругов, m - кол-во точек, L - центр, scale - масштаб dr=1/n#Расстояние df=(2*np.pi)/m#Поворот ax1 = plt.subplot(122) ax2 = plt.subplot(121) ax1.grid(True) ax2.grid(True) k = np.arange(m+1) for i in range(n+1): z=i*dr*np.cos(k*df)+1j*i*dr*np.sin(k*df) w=(z-1)**2 ax1.plot(w.real*scale,w.imag*scale,'.r-') circle=i*dr*np.cos(k*df)+1j*i*dr*np.sin(k*df) ax2.plot(circle.real*scale,circle.imag*scale,'.r-') plt.axis('equal') plt.ylabel('Imaginary') plt.xlabel('Real') plt.show() polar(25, 25) PS я избавился от внутреннего цикла по двум причинам: для удобства соединения точек одной окружности чтобы быстрее считать координаты "векторизованными" NumPy методами

четверг, 9 января 2020 г.

Как отобразить точки пересечения прямой и границы коники

#python #numpy #геометрия #matplotlib #python_27


Пользуясь данный вопросом: https://stackoverflow.com/questions/57312462/how-to-find-and-draw-the-intersection-points-of-contour-shapes
В случае, когда коника представима в виде гиперболы (p,q,v,k,b=5,7,2,3,4 #hyperbole)
программа выдает точку пересечения между прямой, проходящей через точку V и коникой.
Результат работы программы для гиперболы: 

Однако, в случае, когда коника представлена в виде эллипса (p,q,v,k,b=5,0.7,2,3,4
# ellipse) кроме точек, находящихся на границе коники отображаются точки, находящиеся
внутри эллипса. Результат работы программы для эллипса: 

Мой код: 

import numpy as np
import matplotlib.pyplot as plt

def find_roots(x, y):
   s = np.abs(np.diff(np.sign(y))).astype(bool)
   return x[:-1][s] + np.diff(x)[s] / (np.abs(y[1:][s] / y[:-1][s]) + 1)

# p,q,v,k,b=5,7,2,3,4 #hyperbole

p,q,v,k,b=5,0.7,2,3,4 # ellipse

if(((2 * b * k * p) - (2 * b * k * q) + (k * k) - (b * b * q * q)) > 0):
   print("conic is an ellipse")
elif (((2 * b * k * p) - (2 * b * k * q) + (k * k) - (b * b * q * q)) == 0):
   print("conic is a parabola")
else:
   print("conic is a hyperbole")

X = np.arange(-50, 50, 0.05)

plt.plot(-v, 0)
plt.scatter(-v, 0, color='red', marker='o')
plt.text(-v, 0.8, "V", horizontalalignment="center")

xmin, xmax, ymin, ymax = -10, 10, -10, 10
ax = plt.gca()
ax.get_xlim()
ax.set_xlim([xmin, xmax])
ax.set_ylim([ymin, ymax])
ax.spines['left'].set_position('center')
ax.spines['bottom'].set_position('center')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

x, y = np.meshgrid(X, X)
l = b * x + b * v - v * y
vb = plt.contour(x, y, l, [0], colors='red')

conic = x * x * b * 2 * p * k - x * x * b * 2 * q * k + x * x * k * k + y * y - b
* 2 * y + 2 * b * q * x * y
cnc = plt.contour(x, y, (conic), [0], colors='blue')

c = cnc.collections[0].get_paths()[-1]
v0 = c.vertices
x1 = np.sort(v0[:, 0])
y1 = v0[np.argsort(v0[:, 0]), 1]

vb1 = vb.collections[0].get_paths()[0]
v1 = vb1.vertices
x2 = np.sort(v1[:, 0])
y2 = v1[np.argsort(v1[:, 0]), 1]

x = np.linspace(max(x1.min(), x2.min()), min(x1.max(), x2.max()), 1000)

y1i = np.interp(x, x1, y1)
y2i = np.interp(x, x2, y2) 

x_intersect = find_roots(x, y1i - y2i)
y_intersect = np.interp(x_intersect, x, y1i)

plt.plot(x_intersect, y_intersect, marker="X", ms=5, color="limegreen")

plt.show()


Как можно отобразить только точки пересечения прямой. проходящей через точку V и
 границы коники (эллипса)?
    


Ответы

Ответ 1



Задачу можно значительно упростить если работать с двумерными объектами: def graphs_intersection_pos(g1, g2): return np.argwhere(np.diff(np.sign(g1 - g2))).ravel() def line(x): return v*x + b X = np.arange(-50, 50, 0.05) plt.plot(-v, 0) plt.scatter(-v, 0, color='red', marker='o') plt.text(-v, 0.8, "V", horizontalalignment="center") xmin, xmax, ymin, ymax = -10, 10, -10, 10 ax = plt.gca() ax.get_xlim() ax.set_xlim([xmin, xmax]) ax.set_ylim([ymin, ymax]) ax.spines['left'].set_position('center') ax.spines['bottom'].set_position('center') ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) x, y = np.meshgrid(X, X) l = b * x + b * v - v * y vb = plt.contour(x, y, l, [0], colors='red') conic = x * x * b * 2 * p * k - x * x * b * 2 * q * k + x * x * k * k + y * y - b * 2 * y + 2 * b * q * x * y cnc = plt.contour(x, y, (conic), [0], colors='blue') plt.plot(X, line(X), color="red") # X,Y координаты коники / эллипса / контура coords = cnc.allsegs[0][0] # точки прямой должны соответствовать X значениям эллипса x_line = coords[:, 0] y_line = line(x_line) # находим индексы точек пересечения idx = graphs_intersection_pos(coords[:, 1], y_line) plt.scatter(x_line[idx], y_line[idx], color="green", s=50)

воскресенье, 29 декабря 2019 г.

matplotlib разделить маркеры

#python #графика #matplotlib


Есть файл CSV вида 

1,0,enp0s31f6
1,0,enp0s31f6
2,0.286968124,enp0s31f6
3,0.347273802,enp0s31f6
4,0.376051415,enp0s31f6
5,1.895804954,enp0s31f6
6,2.03941807,eth0
7,2.061084575,enp0s31f6
8,3.026748561,enp0s31f6
9,3.247024689,enp0s31f6
10,3.896585252,eth0
11,4.001361629,enp0s31f6
12,4.216054934,enp0s31f6
13,4.26913004,eth0
14,4.26942665,enp0s31f6
15,4.463316773,enp0s31f6


Рисую график вот так:


import matplotlib.pyplot as plt
import csv

x = []
y = []

with open('test.csv','r') as csvfile:
    plots = csv.reader(csvfile, delimiter=',')
    for row in plots:
        x.append(int(row[0]))
        y.append(float(row[1]))

plt.scatter(x,y, marker='o', label='Interfaces')
plt.xlabel('Packets')
plt.ylabel('Time')
plt.title('TEST')
plt.legend()
plt.show()




Получаю на выходе:



Но на данной картинке только 1 вид маркеров (enp0s31f6 и eth0) вместе, как разделить
их? Чтобы , к примеру, enp0s31f6 был синим маркером, а eth0 - оранжевым. 
    


Ответы

Ответ 1



В первую очередь необходимо разделить исходные данные по признаку принадлежности к конкретному интерфейсу. Для этого можно использовать словарь, но так как чтение происходит из csv формата, удобнее будет воспользоваться готовыми инструментами (numpy, pandas). Например, функция recfromcsv из numpy позволяет получить исходные данные в виде numpy.recarray с автоматическим определением типов. import matplotlib.pyplot as plt import numpy as np data = np.recfromcsv('test.csv', encoding=None, names=('time', 'packets', 'interface')) # если в реальности интерфейсов будет больше, то надо добавить цветов colors = 'blue', 'orange' ifaces = np.unique(data.interface) # множество интерфейсов assert len(colors) == ifaces.size for color, iface in zip(colors, ifaces): # получение данных, относящихся к текущему интерфейсу items = data[data.interface == iface] plt.scatter(items.time, items.packets, marker='o', label=iface, color=color) plt.xlabel('Packets') plt.ylabel('Time') plt.title('TEST') plt.legend() plt.show()

Ответ 2



import matplotlib.pyplot as plt import csv x = [] y = [] cl= [] # + with open('test.csv','r') as csvfile: plots = csv.reader(csvfile, delimiter=',') for row in plots: x.append(int(row[0])) y.append(float(row[1])) cl.append('r' if row[2]=='eth0' else 'g') # + plt.scatter(x,y, marker='o', c=cl, label='Interfaces') # + c=cl plt.xlabel('Packets') plt.ylabel('Time') plt.title('TEST') plt.legend() plt.show()

Ответ 3



По-моему проще всего решить эту задачу используя Pandas: import numpy as np import pandas as pd df = pd.read_csv(r'C:\Temp\test.csv', header=None, names=['x','y','name']) df['col'] = np.where(df['name'] == 'enp0s31f6', 'blue', 'orange') df.plot.scatter(x='x', y='y', c=df['col'])

Python (jupyter) анимированный график

#python #matplotlib #jupyter_notebook


Подскажите есть ли возможность в jupyter рисовать анимированные графики, вроде графика
загрузки процессора в реальном времени? Пытаюсь сделать мониторинг времени ответа сервера
на пинг.
Разбираюсь с matplotlib, но то ли он такого не умеет, то ли я не нашел. 

import subprocess
import re
from matplotlib import pyplot as plt

def ping(adress):
    p = subprocess.Popen(["ping.exe",adress], stdout = subprocess.PIPE)
    out = p.communicate()[0]
    t = re.search(r'time=(\d*)',str(out))
    time = t.group().replace('time=','') if t else '0'
    return (time)

x=[0]

%matplotlib inline

for i in range(0,10):
    x.append(ping('ya.ru'))
    plt.plot(x)


График просчитывается весь и отрисовывается весь сразу, подскажите как сделать анимацию
и возможно ли вообще такое?
    


Ответы

Ответ 1



Можно так сделать: import psutil from datetime import datetime as dt from collections import deque import matplotlib.pyplot as plt from matplotlib.dates import date2num import matplotlib.animation as animation %matplotlib notebook N=600 x = deque([date2num(dt.now())], maxlen=N) y = deque([0], maxlen=N) fig, ax = plt.subplots(figsize=(8,3)) line, = ax.plot_date(x, y, marker="") ax.spines['left'].set_visible(False) ax.spines['bottom'].set_visible(False) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) def get_data(): # return ping('ya.ru') return psutil.cpu_percent(.15) def animate(i): x.append(date2num(dt.now())) y.append(get_data()) ax.relim(visible_only=True) ax.autoscale_view(True) line.set_data(x, y) ax.fill_between(x, -0.5, y, color='lightgrey') return line, ani = animation.FuncAnimation(fig, animate, interval=300) #ani.save("d:/temp/test.gif", writer='imagemagick', fps=10) plt.show() Результат:

Ответ 2



в jupyter рисовать анимированные графики, вроде графика загрузки процессора в реальном времени? Рисуем фон и оси графика: %matplotlib inline import matplotlib.pyplot as plt fig, ax = plt.subplots() line, = ax.plot([], [], color='g') time_template = 'time = %s' time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes) Создаём функцию, которая генерирует текущие значения (x, y): import time import psutil # !pip install psutil def frames(): while True: yield time.time(), psutil.cpu_percent(.15) # CPU % over the second обновляем график и экран, используя значения полученные из frames(): import datetime as DT from collections import deque n = 1000 yy = deque([], maxlen=n) xx = deque([], maxlen=n) display_handle = display(None, display_id=True) def animate(args): xx.append(args[0]) yy.append(args[1]) line.set_data(xx, yy) time_text.set_text(time_template % DT.datetime.fromtimestamp(args[0])) ax.relim() # update axes limits ax.autoscale_view(True, True, True) display_handle.update(fig) for x_y in frames(): animate(x_y) Если вам не нужна поддержка Jupyter Lab или достаточно конечный цикл, который как видео можно добавить, то вместо явного display_handler.update() и явного цикла, где вручную animate() вызывается, можно использовать: %matplotlib notebook from matplotlib import animation ani = animation.FuncAnimation(fig, animate, frames=frames) и/или (конечное число кадров): from IPython.display import HTML HTML(ani.to_html5_video())

вторник, 9 июля 2019 г.

Matplotlib. Как реализовать скроллбар?

Всем привет. Пытаюсь изучать язык Python + библиотеку Matplotlib и на данный момент я застрял с некоторыми моментами: https://i.stack.imgur.com/qFz7e.png
Первый:
Я не знаю, как я могу удалить (в окне), нотации (axex: х,Y и их координаты) которые видно на скрине?
Второй:
Я хотел бы знать - могу ли я установить полосу прокрутки на любом графике? Я имею в виду, мне нужна фиксированная Ширина на графике и если содержание (какое-то количество точек или японские свечи) внутри него будут расти(по ширине) - мне нужно появление полосы прокрутки с низу чарта.
Это можно реализовать на нейтивном Python + matplotlib? (без wx Python, tkinter и т.д. и т.п.)
P. S. Буду признателен за любые конструктивные советы.


Ответ

Что касается полосы прокрутки, то ее можно сделать при помощи виджета "Slider", но это будет не совсем просто.
Попробуйте вот такой пример:
from matplotlib.widgets import Slider import math
import matplotlib.pyplot as plt
fig, ax = plt.subplots() plt.subplots_adjust(left=0.25, bottom=0.25)
t = [i / 100. for i in range(0, int(math.pi) * 100, 1)]
s = [math.sin(i * 20) for i in t] l, = plt.plot(t, s, lw=2, color='red') plt.axis([0, 1, -10, 10])
axcolor = 'gray' ax_x_pos = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg=axcolor)
wsize = 10
x_pos = Slider(ax_x_pos, 'Position', 0, len(t) - wsize - 1, valfmt='%d', valinit=0)
ax.set_xlim(t[0], t[wsize]) ax.set_ylim(-1.1, 1.1)
def update(val): print(x_pos.val) pos = int(x_pos.val) ax.set_xlim(t[pos], t[pos + wsize]) fig.canvas.draw_idle()
x_pos.on_changed(update)
plt.show()
Чтобы убрать отображение координат, необходимо заменить функцию вывода на "пустышку":
import matplotlib.pyplot as plt
plt.figure(1) plt.plot([1, 2, 3]) plt.gca().format_coord = lambda x, y: '' plt.show()

среда, 12 июня 2019 г.

визуализация траектории объекта: как добавить текст к точкам на графике

Подскажите пожалуйста как добавить подписи к меткам на траектории. Имеются данные с координатами буя и соответствующая им дата. На рисунке нужно указать даты обозначенных точек.
x,y=lon[1:],lat[1:] plt.figure(figsize=(12,10)) ax = plt.axes(projection=ccrs.Mercator()) ax.set_extent((27.0, 42.0, 40.8, 47.5)) ax.add_feature(LAND) ax.add_feature(OCEAN) ax.add_feature(cfeature.BORDERS, linestyle=':') ax.coastlines('50m') ax.plot(x,y,'b-', transform=ccrs.Geodetic(), marker='o', markevery=10)


Ответ

Я бы воспользовался модулем Pandas:
Пусть у нас есть filename='/path/to/file.csv'
ts,lon,lat 03.07.2006 0:58,32.529,44.008 03.07.2006 3:13,32.51,44.012 03.07.2006 4:50,32.506,44.008 03.07.2006 12:26,32.473,44.01
читаем файл:
import pandas as pd df = pd.read_csv(filename, sep=',', parse_dates=['ts'])
получим:
In [37]: df Out[37]: ts lon lat 0 2006-03-07 00:58:00 32.529 44.008 1 2006-03-07 03:13:00 32.510 44.012 2 2006-03-07 04:50:00 32.506 44.008 3 2006-03-07 12:26:00 32.473 44.010
рисуем точки:
In [38]: ax = df.plot.scatter(x='lon', y='lat')
добавляем надписи:
In [39]: df.apply(lambda x: ax.annotate(x.ts, (x.lon, x.lat)), axis=1) Out[39]: 0 Annotation(32.529,44.008,'2006-03-07 00:58:00') 1 Annotation(32.51,44.012,'2006-03-07 03:13:00') 2 Annotation(32.506,44.008,'2006-03-07 04:50:00') 3 Annotation(32.473,44.01,'2006-03-07 12:26:00') dtype: object
Результат:

PS Вам надо будет подумать как отформатировать дату и время, чтобы они занимали меньше места и о том как уместить эти надписи на графике...

воскресенье, 9 июня 2019 г.

Угол отображения 3d графиков

Графики выглядят как изометрическая проекция, что не очень удобно для оценки того, что происходит по оси delay. Для сравнения "на глаз" было бы хорошо, чтобы ось delay находилась параллельно плоскости монитора, а accum periods перпендикулярно. Как прописать в коде угол, под которым отображаются графики?

x = np.arange (0, 64, 1) y = np.arange (0, aver, 1) xgrid, ygrid = np.meshgrid(x, y) zgrid = ifft_data x, y, z = xgrid,ygrid,zgrid fig = pylab.figure() axes = Axes3D(fig) axes.plot_surface(x, y, z) plt.xlabel('delay') plt.ylabel('accumulation periods') pylab.show()


Ответ

Воспользуйтесь ax.view_init(ax.elev, ax.azim)
Пример:
from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from matplotlib import cm import numpy as np
fig = plt.figure() ax = fig.gca(projection='3d')
# Make data. X = np.arange(-5, 5, 0.25) Y = np.arange(-5, 5, 0.25) X, Y = np.meshgrid(X, Y) R = np.sqrt(X**2 + Y**2) Z = np.sin(R) surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0, antialiased=False)
Результат (по умолчанию):

После поворота:
ax.view_init(30, -89)

PS чтобы найти какие вам нужны значения углов - легче всего начертить график с углами по умолчанию в Jupyter (iPython), повернуть график мышкой так как вам надо и посмотреть какие углы получились:
In [209]: print('elev:\t{}\t\tazim:\t{}'.format(ax.elev, ax.azim)) elev: 30 azim: -89

понедельник, 25 марта 2019 г.

как обновлять график matplotlib в tkinter раз в 5 минут данными из базы данных?

Что нужно: Каждые 5 секунд, считываются координаты из БД и отображаются на графике matplotlib в окне tkinter.
Что сделано: рисуются только первый набор данных и не рисуются второй набор
ВОПРОС: как исправить чтобы рисовался сначала первый набор данных , а через 5 секунд второй?
Вот обновленный минимальный рабочий код:
from threading import Thread from queue import Empty, Queue import time import tkinter as tk from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg#, NavigationToolbar2TkAgg from matplotlib.figure import Figure
class tkChartGUI(tk.Frame):
def __init__(self, parent): tk.Frame.__init__(self, parent) self.parent = parent self.initUI()
def get_latest_data(self, dataid): x_array=[] y_array=[] if (dataid == 1): x_array=[1,2,3,4,5,6,7,8,9,10,11,12,13,14]; y_array=[0.5,0.7,0.3,1.0,0.6,0.9,0.5,0.2,0.1,0.5,0.33,0.55,0.3,0.6] if (dataid == 2): x_array=[1,2,3,4,5,6,7,8,9,10,11,12,13,14]; y_array=[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.1,1.2,1.3] return (x_array, y_array)
def initUI(self): self.parent.title("Simple chart") self.parent.geometry("800x600+300+100")
result_queue = Queue() Thread(target=self.get_latest_data, args=[result_queue], daemon=True).start()
x_array, y_array = self.get_latest_data(1)
f = Figure(figsize=(5, 3), dpi=150) a = f.add_subplot(111) a.set_xlabel("Values_X") a.set_ylabel("Values_Y") a.yaxis.grid(True, which='major') a.xaxis.grid(True, which='major') a.plot(x_array, y_array)
canvas = FigureCanvasTkAgg(f, master=self.parent) canvas.show() canvas.get_tk_widget().grid(row=0,column=0)
def display_result(a, q): x_array = [] y_array = [] try: x_array = q.get(block=False) # get data y_array = q.get(block=False) a.plot(x_array, y_array) canvas.draw() except Empty: #a.clear() timeout_millis = round(100 - (5000 * time.time()) % 100) self.parent.after(timeout_millis, display_result, a, q)
def get_result(q): x_array, y_array = self.get_latest_data(2) q.put(x_array) # put data in FIFO queue x coords array q.put(y_array) # put data in FIFO queue y coords array
display_result(a, result_queue)
def onExit(self): self.quit()
def main(): root = tk.Tk() my_gui = tkChartGUI(root) root.mainloop()
if __name__ == '__main__': main()


Ответ

Чтобы постоянно не считывать данные из базы данных (polling), можно определить trigger, чтобы вызвать функцию (callback), когда в БД интересное событие произойдёт. К примеру, когда в нужную таблицу новое значение добавляется (Launch a Python Script from a sqlite3 Trigger):
CREATE TRIGGER tt AFTER INSERT ON t BEGIN SELECT got_y(NEW.y); END
Обновления графика происходят в got_y() обратном вызове, определённом в make_callback() ниже. Код похож на ответ c loop(), определённой с помощью .after(), с той разницей, что здесь функция вызывается, когда нужные данные готовы, вместо того чтобы периодически непрерывно новые данные запрашивать без блокировки:
#!/usr/bin/env python3 import datetime as DT import sqlite3 import random import threading import time import tkinter as tk from collections import deque
import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.dates import date2num
def generate_data(db): # generate dummy data for the example while True: time.sleep(.1) db.execute('insert into t values(?)', (random.randrange(100),))
def make_callback(root): # plot something # see @MaxU answer https://ru.stackoverflow.com/q/801923/23044 n = 300
def get_t(): return date2num(DT.datetime.now()) xx = deque([get_t()], maxlen=n) yy = deque([0], maxlen=n) fig, ax = plt.subplots() ax.set_ylim(0, 100) line, = ax.plot_date(xx, yy, marker='') format_time = '{:%Y-%m-%d %H:%M:%S}'.format time_text = ax.text(0.5, 0.9, '', transform=ax.transAxes)
# add to GUI canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea. canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
def got_y(y): # update plot xx.append(get_t()) yy.append(int(y)) line.set_data(xx, yy) ax.fill_between(xx, 0, yy, color='lightgrey') time_text.set_text(format_time(DT.datetime.now())) ax.relim() # update axes limits ax.autoscale_view(scaley=False) canvas.draw() return got_y
def main(): root = tk.Tk() # a dummy db for the example db = sqlite3.connect(':memory:', check_same_thread=False) db.execute('create table t(y)') db.create_function('got_y', 1, make_callback(root)) db.execute('CREATE TRIGGER tt AFTER INSERT ON t BEGIN SELECT got_y(NEW.y); END') threading.Thread(target=generate_data, args=[db], daemon=True).start() root.mainloop()
if __name__ == '__main__': main()
Здесь db.create_function() определяет какая Питон-функция используется в качестве got_y(y) обратного вызова. В примере, callback вызывается в дочернем потоке, что не всегда желаемо. Поток с generate_data() используется только для примера, фактически данные могут вставляться в базу данных из другого процесса или других машин. How to receive automatic notifications about changes in tables?

Для сравнения вот вариант с polling, где база данных постоянно опрашивается в фоновом потоке, используя простой цикл (про достоинства и недостатки цикла подробно описано в Как правильно сделать временный цикл?)
def poll_db(interval=5): while True: time.sleep(interval - time.time() % interval) # avoid drift emit(get_y_from_db())
здесь get_y_from_db() делает запрос к базе данных, а emit() генерирует событие для GUI:
#!/usr/bin/env python3 import random import threading import time import tkinter as tk
def get_y_from_db(): # generate dummy data time.sleep(random.random()) # emulate blocking function return random.randrange(100)
def poll_db(emit, interval=5): while True: time.sleep(interval - time.time() % interval) # avoid drift emit(get_y_from_db())
root = tk.Tk() root.bind('<>', lambda e, f=make_callback(root): f(e.y)) # subscribe threading.Thread(target=poll_db, args=[lambda y: root.event_generate('<>', when='tail', y=y)], daemon=True).start() root.mainloop()
Пример кода (где этот метод также используется): вывод процесса показывается в Tkinter GUI с помощью root.event_generate()

Не на всех реализациях можно root.event_generate() в фоновом потоке вызвать. В таких случаях можно использовать queue, чтобы данные между потоками передавать:
#!/usr/bin/env python3 import datetime as DT import random import threading import time import tkinter as tk from collections import deque from queue import Empty, Queue from time import time as timer
import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.dates import date2num
def get_y_from_db(): # generate dummy data time.sleep(random.random()) # emulate blocking function return random.randrange(100)
def poll_db(emit, interval=1): # NOTE: interval is independant from the delay while True: time.sleep(interval) emit(get_y_from_db())
def start_polling_loop(root, q, delay): # plot something # see @MaxU answer https://ru.stackoverflow.com/q/801923/23044 n = 600
def get_t(): return date2num(DT.datetime.now()) xx = deque([get_t()], maxlen=n) yy = deque([0], maxlen=n) fig, ax = plt.subplots() ax.set_ylim(0, 100) line, = ax.plot_date(xx, yy, marker='') format_time = '{:%Y-%m-%d %H:%M:%S}'.format time_text = ax.text(0.5, 0.9, '', transform=ax.transAxes)
# add to GUI canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea. canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
# update in a loop def loop(): timeout_millis = round(delay - (1000 * timer()) % delay) root.after(timeout_millis, loop) # avoid drift
try: y = q.get(block=False) except Empty: return # no new data
# update plot xx.append(get_t()) yy.append(y) line.set_data(xx, yy) ax.fill_between(xx, 0, yy, color='lightgrey') time_text.set_text(format_time(DT.datetime.now())) ax.relim() # update axes limits ax.autoscale_view(scaley=False) canvas.draw() root.after_idle(loop) # start
root = tk.Tk() q = Queue() threading.Thread(target=poll_db, args=[q.put], daemon=True).start() start_polling_loop(root, q, delay=40) root.mainloop()
Упрощённый вариант (с одним обновлением данных из потока), см. в Как сделать постоянное обновление окна Tkinter? Как избежать подвисания на время ожидания ответа от сервера. Ещё пример кода (где этот метод используется): вывод процесса показывается в Tkinter GUI, используя widget.after(), q.get()/q.put()

среда, 13 марта 2019 г.

Python matplotlib Построение гиперболы

Добрый день! Хочу при помощи matplotlib построить, например, гиперболу. Есть следующий код:
import matplotlib.pyplot as plt import pylab, math xmin = -200 xmax = 200 dx = 0.1
xlist = [float(x) for x in range(xmin,xmax)] ylist = [float(1/x) for x in range(xmin,xmax)]
ax = plt.gca() ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') ax.xaxis.set_ticks_position('bottom') ax.spines['bottom'].set_position(('data',0)) ax.yaxis.set_ticks_position('left') ax.spines['left'].set_position(('data',0)) plt.grid(True) plt.title('График функции ') pylab.plot(xlist,ylist) pylab.savefig('123.png')
Пример работы на картинке Как видно, между двумя ветвями функции есть отрезок. Я знаю, что это связанно с тем, что есть значение крайних точек ветвей, а плот их соединяет. Но это мне не нужно, мне нужно получить нормальный график. Если бы этот график был всегда гиперболой, было бы нормально. Но этим графиком может быть любой график с разрывом. Как избежать этого отрезка? Видел способы, но там использовались дополнительные пакеты(numpy), я не хочу тянуть их в этот код []


Ответ

Для начала повторюсь, что matplotlib уже внутри себя использует numpy, так что избежать его не выйдет. Тем не менее, вот первая версия без numpy в основном коде:
import matplotlib.pyplot as plt import decimal
# Это замена np.arange def arange(start, stop, step): while start < stop: yield start start += decimal.Decimal(step)
hyperbola = lambda x: 1 / x
def asymptote_checker(argument, function): try: function(argument) return True except ZeroDivisionError: return False
xmin = -20 xmax = 20 dx = 0.1
xlist = [round(x, 4) for x in arange(xmin, xmax, dx)]
ylist = [hyperbola(x) if asymptote_checker(x, hyperbola) else float('nan') for x in xlist]
plt.plot(xlist, ylist) plt.show()
С numpy выйдет покороче:
import matplotlib.pyplot as plt import decimal import numpy as np
xmin = -20 xmax = 20 dx = 0.1
xlist = np.around(np.arange(xmin, xmax, dx), decimals=4)
ylist = 1 / xlist
plt.plot(xlist, ylist) plt.show()
Пара общих важных моментов для обеих версий: числа в xlist вместо нуля могут внезапно превратиться в мусор типа 7.0485345e-13 или типа того, что выльется в огромное значение по оси Oy. Второй момент - matplotlib игнорирует (и не соединяет точки) числа типа +inf, -inf, NaN - их можно пометить руками, доверить это numpy или использовать numpy masked array

четверг, 7 марта 2019 г.

Как рисовать в Matplotlib в комплексных координатах?

Требуется изобразить вот такую функцию:
w(omega) = 1/(1 + 1j*omega)
в комплексных координатах. Как?


Ответ

Вот такой вариант вам не подойдет?
import numpy as np import matplotlib.pyplot as plt omega = np.linspace(0, 5, 100) w = 1 / (1 + 1j*omega) plt.figure(1) plt.scatter(w.real, w.imag) plt.show()

среда, 20 февраля 2019 г.

Как соединить точки в matplotlib

Всем привет. Перед мной стоит задача соединить точки, но как бы я не пытался ничего не выходит либо точки, либо пустой график.
Как можно соединить точки, так чтобы образовалась сеть?
И вопрос не в тему, почему на втором графике не задается сетка? Задается только если явно в цикле писать.

import matplotlib.pyplot as plt import numpy as np
def polar(n,m,L=(0,0),scale=1): #n - кол-во кругов, m - кол-во точек, L - центр, scale - масштаб dr=1/n#Расстояние df=(2*np.pi)/m#Поворот for i in range(n+1): for k in range(m+1): z=i*dr*np.cos(k*df)+1j*i*dr*np.sin(k*df) w=(z-1)**2 plt.subplot(122) plt.plot(w.real*scale,w.imag*scale,'r.') circle=i*dr*np.cos(k*df)+1j*i*dr*np.sin(k*df) plt.subplot(121) plt.plot(circle.real*scale,circle.imag*scale,'r.') plt.axis('equal') plt.ylabel('Imaginary') plt.xlabel('Real') plt.grid(True) plt.show()
polar(25,25)


Ответ

Если задача - соединить точки, принадлежащие концентрическим окружностям, то можно так сделать:
def polar(n,m,L=(0,0),scale=1): #n - кол-во кругов, m - кол-во точек, L - центр, scale - масштаб dr=1/n#Расстояние df=(2*np.pi)/m#Поворот
ax1 = plt.subplot(122) ax2 = plt.subplot(121) ax1.grid(True) ax2.grid(True)
k = np.arange(m+1)
for i in range(n+1): z=i*dr*np.cos(k*df)+1j*i*dr*np.sin(k*df) w=(z-1)**2 ax1.plot(w.real*scale,w.imag*scale,'.r-') circle=i*dr*np.cos(k*df)+1j*i*dr*np.sin(k*df) ax2.plot(circle.real*scale,circle.imag*scale,'.r-')
plt.axis('equal') plt.ylabel('Imaginary') plt.xlabel('Real') plt.show()
polar(25, 25)

PS я избавился от внутреннего цикла по двум причинам:
для удобства соединения точек одной окружности чтобы быстрее считать координаты "векторизованными" NumPy методами