Попытка подогнать синусоидальную функцию к фазированной кривой блеска

import numpy as np
import matplotlib.pyplot as plt
from lmfit import Model,Parameters


f2= "KELT_N16_lc_006261_V01_west_tfa.dat"    
t2="TIMES"   # file name

NewData2 = np.loadtxt(t2, dtype=float, unpack=True)
NewData = np.loadtxt(f2,dtype=float, unpack=True, usecols=(1,))

flux = NewData   
time= NewData2

new_flux=np.hstack([flux,flux])

# fold
period = 2.0232               # period (must be known already!)

foldTimes = ((time)/ period)  # divide by period to convert to phase
foldTimes = foldTimes % 1   # take fractional part of phase only (i.e. discard whole number part)


new_phase=np.hstack([foldTimes+1,foldTimes])

print len(new_flux)
print len(new_phase)


def Wave(x, new_flux,new_phase):
    wave = new_flux*np.sin(new_phase+x)
    return wave
model = Model(Wave)
print "Independent Vars:", model.independent_vars
print "Parameters:",model.param_names
p = Parameters()
p.add_many(('new_flux',13.42, True, None, None, None) )   
p.add_many(('new_phase',0,True, None, None, None) )   

result=model.fit(new_flux,x=new_phase,params=p,weights= None)


plt.scatter(new_phase,new_flux,marker='o',edgecolors='none',color='blue',s=5.0, label="Period: 2.0232  days")   
plt.ylim([13.42,13.54])
plt.xlim(0,2)
plt.gca().invert_yaxis()
plt.title('HD 240121 Light Curve with BJD Correction')
plt.ylabel('KELT Instrumental Magnitude')
plt.xlabel('Phase')
legend = plt.legend(loc='lower right', shadow=True)
plt.scatter(new_phase,result.best_fit,label="One Oscillation Fit", color='red',s=60.0)
plt.savefig('NewEpoch.png')
print result.fit_report()

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

Вот как теперь выглядит график (попытка подогнать синусоидальную функцию к моему набору данных):

img


person Lauren    schedule 16.12.2016    source источник
comment
Можете ли вы показать, какие ошибки вы получаете? Что теперь дает запуск вашего кода и что вы ожидаете от него?   -  person mba12    schedule 16.12.2016
comment
@mba12 mba12 На данный момент я не получаю сообщение об ошибке, что крайне затрудняет выявление проблемы. Запуск моего кода создает прикрепленный график. Я ожидаю, что он создаст график, на котором соответствие совпадает с данными.   -  person Lauren    schedule 16.12.2016


Ответы (2)


Пара замечаний/предложений:

Во-первых, почти наверняка лучше заменить

p = Parameters()
p.add_many(('new_flux',13.42, True, None, None, None) )
p.add_many(('new_phase',0,True, None, None, None) )

с

p = Parameters()
p.add('new_flux', value=13.42, vary=True)
p.add('new_phase', value=0, vary=True)

Во-вторых, ваша модель не включает смещение постоянного тока, но в ваших данных оно явно есть. Смещение составляет примерно 13,4, а амплитуда синусоиды примерно 0,05. Пока вы это делаете, вы, вероятно, захотите включить масштаб, фазу, а также смещение, чтобы модель

offset + amplitude * sin(scale*x + phase_shift)

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

С более общей моделью вы можете попробовать несколько наборов значений параметров, используя model.eval() для оценки модели с набором параметров. Когда у вас есть лучшая модель и разумные отправные точки, вы должны получить разумную подгонку.

person M Newville    schedule 21.12.2016

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

  • Откуда мы знаем, что есть что и что оно должно делать?
  • Какой метод подгонки вы используете?
  • Где данные и в каком виде?

Я бы начал с вычисления приблизительных параметров синусоидальной волны. Предположим, вы получили входные данные data в виде n точек с x,y координатами. И хочу подогнать греховную волну:

y(t) = y0+A*sin(x0+x(t)*f)

Где y0 — смещение по оси y, x0 — смещение фазы, A — амплитуда, а f — угловая частота.

Я бы:

  1. Вычислить среднее значение

    y0 = sum(data[i].y)/n where i={0,1,2,...n-1}
    

    это среднее значение, представляющее возможное смещение y y0 вашей синусоидальной волны.

  2. вычислить среднее расстояние до y0

    d = sum (|data[i].y-y0|)/n where i={0,1,2,...n-1}
    

    Если мне не изменяет память, это должно быть эффективное значение амплитуды, поэтому:

    A = sqrt(2)*d
    
  3. найти точки пересечения нуля в наборе данных

    для этого набор данных должен быть отсортирован по x, поэтому отсортируйте его, если это не так. Запомните индекс i для: первого пересечения i0, последнего пересечения i1 и количества найденных пересечений j, из этого мы можем оценить частоту и сдвиг фазы:

    f=M_PI*double(j-1)/(datax[i1]-datax[i0]);
    x0=-datax[i0]*f;
    

    Чтобы определить, какую полусинусоидальную волну мы выровняли, просто проверьте знак средней точки между первыми двумя пересечениями нуля.

    i1=i0+((i1-i0)/(j-1));
    if (datay[(i0+i1)>>1]<=y0) x0+=M_PI;
    

    Или вместо этого проверьте конкретную схему пересечения нуля.

    Вот и все, теперь у нас есть примерные x0,y0,f,A параметров синусоиды.

Вот код C++, который я тестировал (извините, я не использую Python):

//---------------------------------------------------------------------------
#include <math.h>
// input data
const int n=5000;
double datax[n];
double datay[n];
// fitted sin wave
double A,x0,y0,f;
//---------------------------------------------------------------------------
void data_generate() // genere random noisy sinvawe
    {
    int i;
    double A=150.0,x0=250.0,y0=200.0,f=0.03,r=20.0;
    double x,y;
    Randomize();
    for (i=0;i<n;i++)
        {
        x=800.0*double(i)/double(n);
        y=y0+A*sin(x0+x*f);
        datax[i]=x+r*Random();
        datay[i]=y+r*Random();
        }
    }
//---------------------------------------------------------------------------
void data_fit() // find raw approximate of x0,y0,f,A
    {
    int i,j,e,i0,i1;
    double x,y,q0,q1;
    // y0 = avg(y)
    for (y0=0.0,i=0;i<n;i++) y0+=datay[i]; y0/=double(n);
    // A = avg(|y-y0|)
    for (A=0.0,i=0;i<n;i++) A+=fabs(datay[i]-y0); A/=double(n); A*=sqrt(2.0);
    // bubble sort data by x asc
    for (e=1,j=n;e;j--)
     for (e=0,i=1;i<j;i++)
      if (datax[i-1]>datax[i])
        {
        x=datax[i-1]; datax[i-1]=datax[i]; datax[i]=x;
        y=datay[i-1]; datay[i-1]=datay[i]; datay[i]=y;
        e=1;
        }
    // find zero crossings
    for (i=0,j=0;i<n;)
        {
        // find value below zero
        for (;i<n;i++) if (datay[i]-y0<=-0.75*A) break; e=i;
        // find value above zero
        for (;i<n;i++) if (datay[i]-y0>=+0.75*A) break;
        if (i>=n) break;
        // find point closest to zero
        for (i1=e;e<i;e++)
         if (fabs(datay[i1]-y0)>fabs(datay[e]-y0)) i1=e;
        if (!j) i0=i1; j++;
        }
    f=2.0*M_PI*double(j-1)/(datax[i1]-datax[i0]);
    x0=-datax[i0]*f;
    }
//---------------------------------------------------------------------------

И превью:

просмотреть

Точки генерируются зашумленными данными, а синяя кривая соответствует синусоидальной волне.

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

person Spektre    schedule 18.12.2016