পান্ডাস: স্থানীয় মিনিমা-ম্যাক্সিমার ভিত্তিতে ডেটা জিগজ্যাগ বিভাজন


10

আমার একটি টাইমরিজ ডেটা আছে। ডেটা তৈরি করা হচ্ছে

date_rng = pd.date_range('2019-01-01', freq='s', periods=400)
df = pd.DataFrame(np.random.lognormal(.005, .5,size=(len(date_rng), 3)),
                  columns=['data1', 'data2', 'data3'],
                  index= date_rng)
s = df['data1']

আমি স্থানীয় ম্যাক্সিমা এবং স্থানীয় মিনিমার মধ্যে সংযুক্ত একটি জিগ-জাগ লাইন তৈরি করতে চাই, যা এই শর্তটি সন্তুষ্ট করে যে |highest - lowest value|প্রতিটি জিগ-জাগ লাইনের পূর্ববর্তী দূরত্বের শতাংশ (20% বলে) ছাড়িয়ে যেতে হবে জিগ-জাগ লাইন, এবং একটি পূর্ব-বর্ণিত মান কে (1.2 বলুন)

আমি এই কোডটি ব্যবহার করে স্থানীয় চূড়ান্ত সন্ধান করতে পারি:

# Find peaks(max).
peak_indexes = signal.argrelextrema(s.values, np.greater)
peak_indexes = peak_indexes[0]

# Find valleys(min).
valley_indexes = signal.argrelextrema(s.values, np.less)
valley_indexes = valley_indexes[0]
# Merge peaks and valleys data points using pandas.
df_peaks = pd.DataFrame({'date': s.index[peak_indexes], 'zigzag_y': s[peak_indexes]})
df_valleys = pd.DataFrame({'date': s.index[valley_indexes], 'zigzag_y': s[valley_indexes]})
df_peaks_valleys = pd.concat([df_peaks, df_valleys], axis=0, ignore_index=True, sort=True)

# Sort peak and valley datapoints by date.
df_peaks_valleys = df_peaks_valleys.sort_values(by=['date'])

তবে এতে কীভাবে প্রান্তিকের শর্তটি প্রয়োগ করতে হয় তা আমি জানি না। দয়া করে আমাকে এই জাতীয় শর্ত প্রয়োগ করতে পরামর্শ দিন।

যেহেতু ডেটাতে মিলিয়ন টাইমস্ট্যাম্প থাকতে পারে, একটি দক্ষ গণনা অত্যন্ত প্রস্তাবিত

পরিষ্কার বর্ণনার জন্য: এখানে চিত্র বর্ণনা লিখুন

উদাহরণস্বরূপ, আমার ডেটা থেকে আউটপুট:

 # Instantiate axes.
(fig, ax) = plt.subplots()
# Plot zigzag trendline.
ax.plot(df_peaks_valleys['date'].values, df_peaks_valleys['zigzag_y'].values, 
                                                        color='red', label="Zigzag")

# Plot original line.
ax.plot(s.index, s, linestyle='dashed', color='black', label="Org. line", linewidth=1)

# Format time.
ax.xaxis_date()
ax.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m-%d"))

plt.gcf().autofmt_xdate()   # Beautify the x-labels
plt.autoscale(tight=True)

plt.legend(loc='best')
plt.grid(True, linestyle='dashed')

এখানে চিত্র বর্ণনা লিখুন

আমার কাঙ্ক্ষিত আউটপুট (এর সাথে মিলিয়ে কিছু, জিগজ্যাগ কেবলমাত্র গুরুত্বপূর্ণ অংশগুলিকে সংযুক্ত করে) এখানে চিত্র বর্ণনা লিখুন

উত্তর:


3

আমি প্রশ্নের আমার সেরা বোঝার উত্তর দিয়েছেন। তবুও এটি কীভাবে পরিবর্তনশীল কে ফিল্টারকে প্রভাবিত করে তা পরিষ্কার নয়।

চলমান অবস্থার উপর ভিত্তি করে আপনি অতিরিক্তটি ফিল্টার করতে চান। আমি ধরে নিয়েছি যে আপনি সেই সমস্ত চূড়ান্ত চিহ্ন চিহ্নিত করতে চান যার শেষ চিহ্নিত চূড়ান্তটির তুলনামূলক দূরত্ব পি% এর চেয়ে বড়। আমি আরও ধরে নিয়েছি যে আপনি সময়সীমার প্রথম উপাদানটিকে সর্বদা একটি বৈধ / প্রাসঙ্গিক বিষয় বিবেচনা করেন।

আমি এটি নিম্নলিখিত ফিল্টার ফাংশন দিয়ে প্রয়োগ করেছি:

def filter(values, percentage):
    previous = values[0] 
    mask = [True]
    for value in values[1:]: 
        relative_difference = np.abs(value - previous)/previous
        if relative_difference > percentage:
            previous = value
            mask.append(True)
        else:
            mask.append(False)
    return mask

আপনার কোডটি চালাতে, আমি প্রথমে নির্ভরতা আমদানি করি:

from scipy import signal
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

কোডটি পুনরায় উত্পাদনযোগ্য করে তুলতে আমি এলোমেলো বীজ ঠিক করি:

np.random.seed(0)

এখান থেকে বাকীটি হ'ল কোপাইপাস্টা। মনে রাখবেন যে ফলাফলটি পরিষ্কার করতে আমি নমুনার পরিমাণ হ্রাস পেয়েছি।

date_rng = pd.date_range('2019-01-01', freq='s', periods=30)
df = pd.DataFrame(np.random.lognormal(.005, .5,size=(len(date_rng), 3)),
                  columns=['data1', 'data2', 'data3'],
                  index= date_rng)
s = df['data1']
# Find peaks(max).
peak_indexes = signal.argrelextrema(s.values, np.greater)
peak_indexes = peak_indexes[0]
# Find valleys(min).
valley_indexes = signal.argrelextrema(s.values, np.less)
valley_indexes = valley_indexes[0]
# Merge peaks and valleys data points using pandas.
df_peaks = pd.DataFrame({'date': s.index[peak_indexes], 'zigzag_y': s[peak_indexes]})
df_valleys = pd.DataFrame({'date': s.index[valley_indexes], 'zigzag_y': s[valley_indexes]})
df_peaks_valleys = pd.concat([df_peaks, df_valleys], axis=0, ignore_index=True, sort=True)
# Sort peak and valley datapoints by date.
df_peaks_valleys = df_peaks_valleys.sort_values(by=['date'])

তারপরে আমরা ফিল্টার ফাংশনটি ব্যবহার করি:

p = 0.2 # 20% 
filter_mask = filter(df_peaks_valleys.zigzag_y, p)
filtered = df_peaks_valleys[filter_mask]

এবং প্লট হিসাবে আপনি আপনার আগের প্লট পাশাপাশি নতুন ফিল্টারড এক্সট্রিমার দুটি করেছিলেন:

 # Instantiate axes.
(fig, ax) = plt.subplots(figsize=(10,10))
# Plot zigzag trendline.
ax.plot(df_peaks_valleys['date'].values, df_peaks_valleys['zigzag_y'].values, 
                                                        color='red', label="Extrema")
# Plot zigzag trendline.
ax.plot(filtered['date'].values, filtered['zigzag_y'].values, 
                                                        color='blue', label="ZigZag")

# Plot original line.
ax.plot(s.index, s, linestyle='dashed', color='black', label="Org. line", linewidth=1)

# Format time.
ax.xaxis_date()
ax.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m-%d"))

plt.gcf().autofmt_xdate()   # Beautify the x-labels
plt.autoscale(tight=True)

plt.legend(loc='best')
plt.grid(True, linestyle='dashed')

এখানে চিত্র বর্ণনা লিখুন

সম্পাদনা :

যদি উভয়ই প্রথম এবং শেষ পয়েন্টটিকে বৈধ হিসাবে বিবেচনা করতে চান তবে আপনি ফিল্টার ফাংশনটি নিম্নরূপে গ্রহণ করতে পারেন:

def filter(values, percentage):
    # the first value is always valid
    previous = values[0] 
    mask = [True]
    # evaluate all points from the second to (n-1)th
    for value in values[1:-1]: 
        relative_difference = np.abs(value - previous)/previous
        if relative_difference > percentage:
            previous = value
            mask.append(True)
        else:
            mask.append(False)
    # the last value is always valid
    mask.append(True)
    return mask

হাই, উত্তরের উত্তরের জন্য ধন্যবাদ। হ্যাঁ আপনার অনুমানটি সঠিক "সর্বশেষ চিহ্নিত চূড়ান্তটির তুলনামূলক দূরত্ব পি% এর চেয়ে বড়।" এবং সমস্ত প্রথম এবং শেষ পয়েন্ট উভয়ই বিবেচনা করা উচিত all আমি আপনার উত্তরটি যাচাই করেছি, কখনও কখনও এটি শেষ পয়েন্টটি মিস করে, আপনি কি আমাকে এটিতে সহায়তা করতে পারেন?
থানহ এনগুইন

3

স্থানীয় অতিরিক্ত তৈরি করতে আপনি পান্ডাস রোলিং কার্যকারিতা ব্যবহার করতে পারেন। এটি আপনার স্কিপি পদ্ধতির তুলনায় কোডটিকে কিছুটা সহজ করে।

অতিরিক্ত খুঁজে বের করার জন্য কার্যাদি:

def islocalmax(x):
    """Both neighbors are lower,
    assumes a centered window of size 3"""
    return (x[0] < x[1]) & (x[2] < x[1])

def islocalmin(x):
    """Both neighbors are higher,
    assumes a centered window of size 3"""
    return (x[0] > x[1]) & (x[2] > x[1])

def isextrema(x):
    return islocalmax(x) or islocalmin(x)

জিগজ্যাগ তৈরির কাজটি, এটি একবারে (প্রতিটি কলামের উপরে) ডেটাফ্রেমে প্রয়োগ করা যেতে পারে তবে এটি এনএএন'র সাথে পরিচয় করিয়ে দেবে যেহেতু প্রতিটি কলামের জন্য প্রত্যাবর্তিত টাইমস্ট্যাম্পগুলি আলাদা হবে। নীচের উদাহরণে প্রদর্শিত হিসাবে আপনি পরে এগুলি সহজেই ফেলে দিতে পারেন বা কেবল আপনার ডেটাফ্রেমে একক কলামে ফাংশনটি প্রয়োগ করতে পারেন।

নোট করুন যে আমি একটি থ্রোসোল্ডের বিরুদ্ধে পরীক্ষাটি uncommented করেছি k, আমি নিশ্চিত না যে অংশটি পুরোপুরি বুঝতে পেরেছি কিনা I'm পূর্বের এবং বর্তমান চরমের মধ্যে পরম পার্থক্য যদি এর চেয়ে বড় হওয়া দরকার হয় তবে আপনি এটি অন্তর্ভুক্ত করতে পারেন k:& (ext_val.diff().abs() > k)

আমি নিশ্চিতও নই যে চূড়ান্ত জিগজ্যাগটি সর্বদা একটি আসল উচ্চ থেকে নিম্ন বা বিপরীতে চলে যাওয়া উচিত। আমি ধরে নিয়েছিলাম এটি করা উচিত, অন্যথায় আপনি ফাংশনটির শেষে দ্বিতীয়টি চরমের জন্য সরাতে পারেন।

def create_zigzag(col, p=0.2, k=1.2):

    # Find the local min/max
    # converting to bool converts NaN to True, which makes it include the endpoints    
    ext_loc = col.rolling(3, center=True).apply(isextrema, raw=False).astype(np.bool_)

    # extract values at local min/max
    ext_val = col[ext_loc]

    # filter locations based on threshold
    thres_ext_loc = (ext_val.diff().abs() > (ext_val.shift(-1).abs() * p)) #& (ext_val.diff().abs() > k)

    # Keep the endpoints
    thres_ext_loc.iloc[0] = True
    thres_ext_loc.iloc[-1] = True

    thres_ext_loc = thres_ext_loc[thres_ext_loc]

    # extract values at filtered locations 
    thres_ext_val = col.loc[thres_ext_loc.index]

    # again search the extrema to force the zigzag to always go from high > low or vice versa,
    # never low > low, or high > high
    ext_loc = thres_ext_val.rolling(3, center=True).apply(isextrema, raw=False).astype(np.bool_)
    thres_ext_val  =thres_ext_val[ext_loc]

    return thres_ext_val

কিছু নমুনা ডেটা তৈরি করুন:

date_rng = pd.date_range('2019-01-01', freq='s', periods=35)

df = pd.DataFrame(np.random.randn(len(date_rng), 3),
                  columns=['data1', 'data2', 'data3'],
                  index= date_rng)

df = df.cumsum()

ফাংশনটি প্রয়োগ করুন এবং 'ডেটা 1' কলামের জন্য ফলাফলটি বের করুন:

dfzigzag = df.apply(create_zigzag)
data1_zigzag = dfzigzag['data1'].dropna()

ফলাফলটি দেখুন:

fig, axs = plt.subplots(figsize=(10, 3))

axs.plot(df.data1, 'ko-', ms=4, label='original')
axs.plot(data1_zigzag, 'ro-', ms=4, label='zigzag')
axs.legend()

এখানে চিত্র বর্ণনা লিখুন


আপনার উত্তরের জন্য ধন্যবাদ. আমি এই লাইনটি সম্পর্কে জিজ্ঞাসা করতে চাই (ext_val.diff().abs() > (ext_val.shift(-1).abs() * p)), যেমনটি আমি বুঝতে পেরেছি আপনি p%শেষ পয়েন্টের সাথে দুটি পয়েন্টের সাথে দূরত্বটি তুলনা করছেন , আমি কি ঠিক আছি? কারণ আমি প্রতিটি জিগজ্যাগ সেগমেন্টটি পূর্ববর্তী বিভাগের সাথে তুলনা করতে চাই এবং শর্তটি সন্তুষ্ট না হওয়া পর্যন্ত পুনরাবৃত্তি করি।
থানহ এনগুইন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.