Как подогнать несколько кривых к одному графику разброса данных? - PullRequest
2 голосов
/ 11 января 2020

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

This is an example of what the data looks like

Я знаю, что каждая из отдельных кривых находится под семья A / x + B. На данный момент я вырезал каждую кривую вручную и подгонку кривой, но хотел бы автоматизировать этот процесс, чтобы компьютер разделял эти кривые под их подгонку. Я пытался использовать машинное обучение, но не знал, с чего начать, какие пакеты использовать. Я использую python, но могу также использовать C ++, на самом деле я надеюсь перенести его в C ++ к концу. Как вы думаете, с чего мне начать, стоит ли использовать автоматическое машинное обучение без присмотра или есть лучший способ для разделения данных?

Ожидаемые кривые:

I hope to end with equations for these lines

Пример данных

Ответы [ 3 ]

1 голос
/ 11 января 2020

Я извлек данные из графика для анализа. Вот пример кода, который загружает, разделяет, помещает и отображает три набора данных. Работает, когда отдельные файлы данных добавляются в один текстовый файл.

plot

import numpy, scipy, matplotlib
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit


##########################################################
# data load and separation section

datafilename = 'temp.dat'
textdata = open(datafilename, 'rt').read()


xLists = [[], [], []]
yLists = [[], [], []]

previousY = 0.0 # initialize
whichList = -1 # initialize
datalines = textdata.split('\n')
for line in datalines:
    if not line: # allow for blank lines in data file
        continue
    spl = line.split()
    x = float(spl[0])
    y = float(spl[1])

    if y > previousY + 50.0: # this separator must be greater than max noise
        whichList += 1
    previousY = y

    xLists[whichList].append(x)
    yLists[whichList].append(y)



##########################################################
# curve fitting section

def func(x, a, b):
    return a / x + b


parameterLists = []
for curveIndex in range(len(xLists)):
    # these are the same as the scipy defaults
    initialParameters = numpy.array([1.0, 1.0])

    xData = numpy.array(xLists[curveIndex], dtype=float)
    yData = numpy.array(yLists[curveIndex], dtype=float)

    # curve fit the test data
    fittedParameters, pcov = curve_fit(func, xData, yData, initialParameters)

    parameterLists.append(fittedParameters)


##########################################################
# graphics output section
def ModelAndScatterPlot(graphWidth, graphHeight):
    f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)
    axes = f.add_subplot(111)

    for curveIndex in range(len(xLists)):
        # first the raw data as a scatter plot
        axes.plot(xLists[curveIndex], yLists[curveIndex],  'D')

        # create data for each fitted equation plot
        xModel = numpy.linspace(min(xLists[curveIndex]), max(xLists[curveIndex]))
        yModel = func(xModel, *parameterLists[curveIndex])

        # now the model as a line plot
        axes.plot(xModel, yModel)

    axes.set_xlabel('X Data') # X axis data label
    axes.set_ylabel('Y Data') # Y axis data label

    plt.show()
    plt.close('all') # clean up after using pyplot

graphWidth = 800
graphHeight = 600
ModelAndScatterPlot(graphWidth, graphHeight)
1 голос
/ 11 января 2020

Что ж, у вас наверняка есть интересная проблема.

Я вижу, что есть кривые со значениями оси Y, которые значительно больше, чем остальные. Я просто взял бы первые N-значения с самыми большими значениями оси Y и затем поместил бы их в кривую экспоненциального затухания (или ту другую кривую, которую вы упоминаете). Затем вы можете просто взять точки, которые наиболее соответствуют этой кривой, а затем оставить остальные точки в покое.

Кроме ...

Это ужасный способ экстраполяции данных. Делая это, вы черпаете данные, которые хотите. Это фальсифицирующая информация, и она очень плохая.

Лучше всего создать единую кривую, подходящую для всех точек, если вы не можете выделить все эти точки в отдельные кривые с внешней информацией.

Но ...

Нам известна некоторая информация: допустимая функция должна иметь только 1 выход на один вход.

Если ось X является дискретной, это означает, что вы можете создать поиск Таблица выходов с учетом ввода. Это позволяет подсчитать, сколько там кривых связано с указанным значением c X (которое может быть единицей времени). Другими словами, вы должны иметь внешнюю информацию для локального разделения точек. Затем вы можете изменить порядок точек при увеличении значения Y, и теперь у вас есть отдельные кривые, определенные в дискретных точках.

По сути, это неразрешимая проблема в общем смысле, но в вашем конкретном приложении c , могут быть дополнительные правила, которые дополнительно определяют домен и диапазон, так что вы можете выполнять фильтрацию данных.

Еще одна вещь ...

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

Если вы используете такие вещи, как unum numbers , вы можете хранить достаточно информации в десятичном виде, так что Ваши функции подгонки могут различать точки без дополнительной фильтрации.

Этот случай - большая надежда, чем что-либо еще, поскольку принятие нового числового представления для обеспечения большей точности при выделении точек выборки в лучшем случае растягивается.

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

  1. Boost.uBLAS
  2. Eigen
  3. LAPACK ++

Надеюсь, я дал вам достаточно информации, чтобы вы могли решить твоя проблема.

0 голосов
/ 11 января 2020

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

  • Приближения являются аналитическими приближениями с использованием этих двух уравнений, которые я вывел:

enter image description here

enter image description here

где (x1, y1) и (x2, y2) - координаты двух точек на кривой.

  • К получить эти две точки Я предположил , что (1) первые точки (в соответствии с осью х) распределены поровну между различными действительными кривыми. И (2) две первые точки каждой реальной кривой меньше или больше, чем две первые точки каждой реальной кривой. Таким образом, их сортировка и деление на N групп будет успешно кластеризовать первые * 2 * N * точек. Если эти предположения неверны вы все равно можете вручную классифицировать 2 первые точки каждой реальной кривой, а остальные будут классифицироваться автоматически (это фактически первый подход, который я реализовал).

  • Затем скопируйте остальные точки в ближайшем приближении каждой точки. Ближайшее значение с наименьшей ошибкой.

Редактировать: Более сильный подход для начального приближения мог бы быть вычислением A и B для пары пар точек и используя их среднее значение A и B в качестве приближения. И, возможно, даже возможно использование K-средних в этих точках / приближениях.

Код:

import numpy as np
import matplotlib.pyplot as plt

# You should probably edit this variable
NUM_OF_CURVES = 4

# <data> should be a 1-D array containing the Y values of the series
# <x_of_data> should be a 1-D array containing the corresponding X values of the series
data, x_of_data = np.loadtxt('...')

# clustering of first 2*num_of_curves points
# I started at NUM_OF_CURVES instead of 0 because my xs started at 0. 
#     The range (0:NUM_OF_CURVES*2) will probably be better for you.
raw_data = data[NUM_OF_CURVES:NUM_OF_CURVES*3]
raw_xs = x_of_data[NUM_OF_CURVES:NUM_OF_CURVES*3]
sort_ind = np.argsort(raw_data)
Y = raw_data[sort_ind].reshape(NUM_OF_CURVES,-1).T
X = raw_xs[sort_ind].reshape(NUM_OF_CURVES,-1).T

# approximation of A and B for each curve
A = ((Y[0]*Y[1])*(X[0]-X[1]))/(Y[1]-Y[0])  
B = (A / Y[0]) - X[0] 

# creating approximating curves
f = []
for i in range(NUM_OF_CURVES):
    f.append(A[i]/(x_of_data+B[i]))
curves = np.vstack(f)

# clustering the points to the approximating curves
raw_clusters = [[] for _ in range(NUM_OF_CURVES)]
for i in range(len(data)):
    raw_clusters[np.abs(curves[:,i]-data[i]).argmin()].append((x_of_data[i],data[i]))

# changing the clusters to np.arrays of the shape (2,-1) 
# where row 0 contains the X coordinates and row 1 the Y coordinates
clusters = []
for i in range(len(raw_clusters)):
    clusters.append(np.array(list(zip(*raw_clusters[i]))))

Пример:

необработанные серии: enter image description here

отдельные серии: enter image description here

...