ریئل ٹائم سیریز کا ڈیٹا شاذ و نادر ہی صاف ہوتا ہے۔ سینسر کی بندش، سسٹم کلاک ڈرفٹ، ڈپلیکیٹ پائپ لائن ریکارڈ، اور دستی ڈیٹا انٹری غلطیوں کا سبب بنتی ہے۔ جب تک ڈیٹا سیٹ آپ کے لیپ ٹاپ تک پہنچتا ہے، اسے جمع، منتقل، اور ذخیرہ کیا جاتا ہے، ہر قدم بدعنوانی کا ممکنہ ذریعہ ہوتا ہے۔
ٹائم سیریز ڈیٹا کو ترتیب دینا ٹیبلر ڈیٹا کو منظم کرنے سے زیادہ مشکل ہے کیونکہ وقت ایک ساختی رکاوٹ ہے۔ آپ ماضی کے مشاہدات میں مستقبل کے ڈیٹا کو لائے بغیر قطاروں کو شفل نہیں کر سکتے یا کالم کے ذرائع کے ساتھ گمشدہ اقدار کو نہیں لگا سکتے۔ صفائی کے تمام فیصلوں کو وقتی ترتیب کا احترام کرنا چاہیے۔ دوسری صورت میں، اس کے اوپر بنی ہوئی ہر چیز کی سالمیت ٹوٹ جاتی ہے۔
یہ گائیڈ آپ کو Python میں کلین اپ پائپ لائن کے ذریعے، خام ڈیٹا کی آمد سے لے کر فیچر انجینئرنگ یا ماڈلنگ کے لیے تیار کردہ ڈیٹا سیٹ تک لے جاتا ہے۔ اس میں گمشدہ قدر کا پتہ لگانے اور ان کی شناخت، آؤٹ لیئر شناخت اور پروسیسنگ، فالتو پروسیسنگ، فریکوئنسی چھانٹنا، شور کو ہموار کرنا، اور تمام نمونے والے سینسر ڈیٹا پر لاگو اسکیما کی توثیق کا احاطہ کیا گیا ہے۔
آپ GitHub سے Colab نوٹ بک ڈاؤن لوڈ کر کے ساتھ ساتھ چل سکتے ہیں۔
شرطیں
اس گائیڈ پر عمل کرنے کے لیے، آپ کو درج ذیل تقاضوں کو پورا کرنا ہوگا۔
-
Python اور Pandas DataFrames کے ساتھ آرام سے کام کریں۔
-
ٹائم انڈیکسڈ ڈیٹا سے واقف
-
اعلی سطح پر فیچر انجینئرنگ اور مشین لرننگ ماڈلنگ میں کیا شامل ہے اس کو پہچانیں۔
ہم استعمال کریں گے pandas اور numpy ڈیٹا ہیرا پھیری کے لیے، scipy سگنل کو ہموار کرنے اور شماریاتی جانچ کے لیے scikit-learn غیر معمولی کا پتہ لگانے کے لئے statsmodels موسمی خرابی کے لیے۔ اس گائیڈ میں کوڈ چلانے سے پہلے اسے انسٹال کریں۔
pip install pandas numpy scipy scikit-learn statsmodels
انڈیکس
اسے صاف کرنے سے پہلے ٹائم سیریز کا آڈٹ کیسے کریں۔
ڈیٹا کی صفائی کا پہلا اصول یہ ہے کہ آپ کو کاٹنے سے پہلے دیکھیں۔ کسی بھی چیز کو تبدیل کرنے، ہموار کرنے یا حذف کرنے سے پہلے، آپ کو اس بات کی مکمل تصویر درکار ہے کہ کیا غلط ہے اور مسئلہ کہاں ہے۔
ایک اچھا شکریہ آپ میں شامل ہے:
-
ٹائم انڈیکس: کیا یہ باقاعدہ ہے؟ کیا کوئی خالی جگہیں ہیں؟
-
گمشدہ اقدار کی تقسیم: کیا گمشدہ اقدار بے ترتیب ہیں یا کلسٹرڈ؟
-
قدر کی حد: کیا کوئی قابل توجہ خلا یا سینسر کی خرابیاں ہیں؟
-
ڈپلیکیٹ ٹائم اسٹیمپ۔
آئیے ایک نمونہ ڈیٹاسیٹ چلاتے ہیں جس میں مندرجہ بالا مسائل میں سے کچھ شامل ہیں۔
# Simulate one week of smart grid voltage readings (hourly)
# with realistic problems injected
periods = 168
index = pd.date_range("2024-06-01", periods=periods, freq="H")
voltage = (
230.0
+ 3.5 * np.sin(2 * np.pi * np.arange(periods) / 24)
+ np.random.normal(0, 1.2, periods)
)
# Inject problems
voltage[14:17] = np.nan # sensor dropout: 3 consecutive missing
voltage[42] = np.nan # isolated missing
voltage[78] = 312.4 # spike outlier
voltage[101:104] = np.nan # another dropout
voltage[130] = 187.2 # dip outlier
series = pd.Series(voltage, index=index, name="voltage_v")
# --- Audit ---
print("=== TIME SERIES AUDIT ===")
print(f"Period: {series.index.min()} → {series.index.max()}")
print(f"Observations: {len(series)}")
print(f"Expected freq: {pd.infer_freq(series.index)}")
print(f"\nMissing values: {series.isna().sum()} ({series.isna().mean()*100:.1f}%)")
print(f"Value range: [{series.min():.2f}, {series.max():.2f}]")
print(f"Mean ± Std: {series.mean():.2f} ± {series.std():.2f}")
# Identify consecutive missing runs
missing_mask = series.isna()
missing_runs = []
run_start = None
for i, (ts, is_missing) in enumerate(missing_mask.items()):
if is_missing and run_start is None:
run_start = ts
elif not is_missing and run_start is not None:
missing_runs.append((run_start, missing_mask.index[i - 1]))
run_start = None
print(f"\nMissing runs ({len(missing_runs)} total):")
for start, end in missing_runs:
print(f" {start} → {end}")
حساب کتاب:
=== TIME SERIES AUDIT ===
Period: 2024-06-01 00:00:00 → 2024-06-07 23:00:00
Observations: 168
Expected freq: h
Missing values: 7 (4.2%)
Value range: [187.20, 312.40]
Mean ± Std: 230.22 ± 7.81
Missing runs (3 total):
2024-06-01 14:00:00 → 2024-06-01 16:00:00
2024-06-02 18:00:00 → 2024-06-02 18:00:00
2024-06-05 05:00:00 → 2024-06-05 07:00:00
صفائی شروع کرنے سے پہلے یہ آڈٹ آپ کو نقصان کا نقشہ دے گا۔ اہم کام کے درمیان فرق کرنا ہے: الگ تھلگ غائب قدریہ مقامی سیاق و سباق سے منسوب کیا جا سکتا ہے؛ لمبی دوری کی دوڑیں غائب ہیں۔اس کے لیے نیچے دھارے کے صارفین کے لیے مختلف حکمت عملیوں یا جھنڈوں کی ضرورت پڑ سکتی ہے۔
معیاری فریکوئنسی پر ری انڈیکس کیسے کریں۔
لاپتہ اقدار کو شمار کرنے سے پہلے، ٹائم انڈیکس اصل میں ہے باقاعدہ. جمع شدہ ٹائم سیریز کے ساتھ ایک عام مسئلہ یہ ہے کہ گمشدہ ٹائم اسٹیمپ اس طرح دکھائے جانے کے بجائے صرف غائب ہیں: NaN قطار – اس کا مطلب ہے: .fillna() اگر آپ فون کریں گے تو آپ کو کبھی نہیں ملے گا۔
# Simulate a sensor feed with missing timestamps (not just missing values)
irregular_index = index.delete([14, 15, 16, 42, 101, 102, 103])
irregular_series = series.dropna().reindex(irregular_index)
print(f"Original length: {len(series)}")
print(f"Irregular length: {len(irregular_series)}")
print(f"Inferred freq: {pd.infer_freq(irregular_series.index)}") # None = irregular
# Reindex to the full canonical hourly grid
canonical_index = pd.date_range(
start=irregular_series.index.min(),
end=irregular_series.index.max(),
freq="H"
)
reindexed = irregular_series.reindex(canonical_index)
print(f"\nAfter reindex:")
print(f"Length: {len(reindexed)}")
print(f"Missing values: {reindexed.isna().sum()}")
print(f"Inferred freq: {pd.infer_freq(reindexed.index)}")
حساب کتاب:
Original length: 168
Irregular length: 161
Inferred freq: None
After reindex:
Length: 168
Missing values: 7
Inferred freq: h
pd.infer_freq واپس آ رہا ہے None یہ ایک اشارہ ہے کہ ایکسپوننٹ میں خلا ہے۔ معیاری گرڈ میں دوبارہ انڈیکس کرنے کے بعد، غائب ٹائم سٹیمپ واضح طور پر ظاہر ہوتے ہیں۔ NaN اب ہم اسے متبادل منطق کا استعمال کرتے ہوئے تلاش کر سکتے ہیں۔
گمشدہ اقدار کو کیسے ہینڈل کریں۔
تمام گمشدہ اقدار کے ساتھ ایک جیسا سلوک نہیں کیا جانا چاہیے۔ ہموار سگنلز میں، واحد الگ تھلگ گمشدہ ریڈنگ کو انٹرپولیشن کے ذریعے بہترین طریقے سے بھرا جاتا ہے۔ تاہم، اتار چڑھاؤ والے سگنل کا 3 گھنٹے کا سینسر ڈراپ آؤٹ ہیرا پھیری سے زیادہ دکھائی دے سکتا ہے۔ حکمت عملی کو وقفہ کی لمبائی اور سگنل کے رویے دونوں سے مماثل ہونا چاہیے۔
فارورڈ فل – سٹیپ فنکشن سگنلز کے لیے
فارورڈ اسٹفنگ اس وقت مناسب ہے جب کوئی متغیر اپنی آخری معلوم قدر کو برقرار رکھتا ہے جب تک کہ یہ تبدیل نہ ہوجائے، جیسے مشین کی حالت، سیٹ پوائنٹ، یا واضح پرچم۔
# Equipment operating mode — a step signal
mode_data = pd.Series(
["running", "running", np.nan, np.nan, "idle", "idle", np.nan, "running"],
index=pd.date_range("2024-06-01", periods=8, freq="H"),
name="operating_mode"
)
filled_mode = mode_data.ffill()
print(pd.DataFrame({"original": mode_data, "ffill": filled_mode}))
حساب کتاب:
original ffill
2024-06-01 00:00:00 running running
2024-06-01 01:00:00 running running
2024-06-01 02:00:00 NaN running
2024-06-01 03:00:00 NaN running
2024-06-01 04:00:00 idle idle
2024-06-01 05:00:00 idle idle
2024-06-01 06:00:00 NaN idle
2024-06-01 07:00:00 running running
وقت کا وزن والا انٹرپولیشن – مسلسل سگنلز کے لیے
مسلسل سینسر ریڈنگ کے لیے، ٹائم ویٹڈ لکیری انٹرپولیشن فاسد فاصلہ کو درست طریقے سے ہینڈل کرتا ہے کیونکہ یہ یہ نہیں مانتا کہ وقفہ برابر ہے۔
# Fill the voltage series using time-based interpolation
voltage_clean = reindexed.interpolate(method="time")
# Compare original vs filled around the first gap
gap_window = voltage_clean["2024-06-01 12:00":"2024-06-01 18:00"]
original_window = reindexed["2024-06-01 12:00":"2024-06-01 18:00"]
comparison = pd.DataFrame({
"original": original_window,
"interpolated": gap_window.round(3),
"was_missing": original_window.isna(),
})
print(comparison)
حساب کتاب:
original interpolated was_missing
2024-06-01 12:00:00 230.290355 230.290 False
2024-06-01 13:00:00 226.798197 226.798 False
2024-06-01 14:00:00 NaN 226.848 True
2024-06-01 15:00:00 NaN 226.897 True
2024-06-01 16:00:00 NaN 226.947 True
2024-06-01 17:00:00 226.996356 226.996 False
2024-06-01 18:00:00 225.410371 225.410 False
موسمی سڑن کا الزام – طویل وقفوں کے لیے
موسمی سگنل میں چند قدموں سے لمبے وقفوں کے لیے، وقفہ کو انٹرپول کرنا موسمی پیٹرن کو نظر انداز کرتا ہے۔ ایک بہتر طریقہ یہ ہے کہ سیریز کو گلا جائے، ہر جزو کو انفرادی طور پر لگایا جائے، اور پھر دوبارہ تشکیل دیا جائے۔
from statsmodels.tsa.seasonal import seasonal_decompose
# Use a longer series for decomposition (needs enough periods)
long_voltage = pd.Series(
230.0
+ 3.5 * np.sin(2 * np.pi * np.arange(336) / 24)
+ np.random.normal(0, 1.0, 336),
index=pd.date_range("2024-06-01", periods=336, freq="H")
)
# Inject a 6-hour gap
long_voltage.iloc[100:106] = np.nan
# Interpolate first to give decompose a complete series to work with
temp_filled = long_voltage.interpolate(method="time")
decomp = seasonal_decompose(temp_filled, model="additive", period=24)
# Reconstruct: trend + seasonal + zero residual for missing positions
reconstructed = long_voltage.copy()
missing_idx = long_voltage[long_voltage.isna()].index
reconstructed[missing_idx] = (
decomp.trend[missing_idx].fillna(method="ffill")
+ decomp.seasonal[missing_idx]
)
print(f"Missing before: {long_voltage.isna().sum()}")
print(f"Missing after: {reconstructed.isna().sum()}")
print("\nFilled values at gap:")
print(reconstructed[missing_idx].round(3))
حساب کتاب:
original interpolated was_missing
2024-06-01 12:00:00 230.290355 230.290 False
2024-06-01 13:00:00 226.798197 226.798 False
2024-06-01 14:00:00 NaN 226.848 True
2024-06-01 15:00:00 NaN 226.897 True
2024-06-01 16:00:00 NaN 226.947 True
2024-06-01 17:00:00 226.996356 226.996 False
2024-06-01 18:00:00 225.410371 225.410 False
موسمی سڑن کا متبادل دن کے وقت کے پیٹرن کی پیروی کرتا ہے۔ جیسا کہ آپ دیکھ سکتے ہیں، بھری ہوئی قدریں وقفہ کے دوران ایک فلیٹ لائن کے بجائے متوقع یومیہ منحنی خطوط کی پیروی کرتی ہیں۔
باہر جانے والوں کا پتہ لگانے اور ان سے نمٹنے کا طریقہ
ٹائم سیریز میں آؤٹ لیرز ٹیبلر ڈیٹا کی نسبت زیادہ چیلنجنگ ہیں کیونکہ سیاق و سباق اہم ہے۔ مثال کے طور پر، غیر معمولی طور پر زیادہ یا کم وولٹیج سینسر اسپائکس یا حقیقی گرڈ واقعات ہو سکتے ہیں۔ مجھے استعمال کرنے کا ایک طریقہ درکار ہے: وقتی سیاق و سباقیہ صرف عالمی اعداد و شمار نہیں ہیں۔
رولنگ ونڈو کے ساتھ زیڈ سکور
عالمی Z-اسکور بے ضابطگیوں کی سیریز میں مقامی بے ضابطگیوں کو یاد کرتا ہے۔ رولنگ زیڈ اسکورز غیر معمولی اقدار کو ظاہر کرتے ہیں۔ اپنے مقامی پڑوسیوں کے مقابلے میں.
میمو: کوئی راستہ نہیں۔ نان سٹیشنری سیریز ایک وقتی سلسلہ جس میں شماریاتی خصوصیات جیسے کہ وسط، تغیر، اور رجحان مستقل نہیں رہتے بلکہ وقت کے ساتھ بدلتے رہتے ہیں۔
window = 24 # 24-hour rolling window
roll_mean = voltage_clean.rolling(window, center=True, min_periods=1).mean()
roll_std = voltage_clean.rolling(window, center=True, min_periods=1).std()
rolling_z = (voltage_clean - roll_mean) / roll_std
threshold = 3.0
outliers_z = rolling_z[rolling_z.abs() > threshold]
print(f"Rolling Z-score outliers detected: {len(outliers_z)}")
print(outliers_z.round(3))
حساب کتاب:
Rolling Z-score outliers detected: 2
2024-06-04 06:00:00 4.646
2024-06-06 10:00:00 -4.484
Name: voltage_v, dtype: float64
Z-score outlier کا پتہ لگانا تقریباً Gaussian (عام) تقسیموں کے لیے بہترین موزوں ہے کیونکہ یہ فرض کرتا ہے کہ ڈیٹا معیاری انحراف سے ماپا جانے والے ہم آہنگی پھیلاؤ کے ساتھ وسط کے گرد مرکوز ہے۔
IQR پر مبنی آؤٹ لیئر کا پتہ لگانا
انٹرکوارٹائل رینج (IQR) طریقہ غیر گاوسی تقسیموں میں آؤٹ لیرز کا پتہ لگانے کے لیے زیادہ طاقتور ہے۔ انٹرکوارٹائل رینج (IQR) تیسرے چوتھائی (Q3) اور پہلے کوارٹائل (Q1) کے درمیان فرق ہے، جو ڈیٹا کے درمیانی 50% کی تقسیم کی نمائندگی کرتا ہے۔
Q1 = voltage_clean.quantile(0.25)
Q3 = voltage_clean.quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers_iqr = voltage_clean[
(voltage_clean < lower_bound) | (voltage_clean > upper_bound)
]
print(f"IQR bounds: [{lower_bound:.2f}, {upper_bound:.2f}]")
print(f"Outliers detected: {len(outliers_iqr)}")
print(outliers_iqr.round(2))
حساب کتاب:
IQR bounds: [220.16, 239.46]
Outliers detected: 2
2024-06-04 06:00:00 312.4
2024-06-06 10:00:00 187.2
Name: voltage_v, dtype: float64
آئسولیشن فاریسٹ – ملٹی ویریٹ آؤٹ لیئر کا پتہ لگانے کے لیے
ایک سے زیادہ سینسر کے ساتھ، اکیلے ایک چینل کی ریڈنگ عام دکھائی دے سکتی ہے، لیکن جب دوسرے چینلز کی ریڈنگز کے ساتھ ملایا جائے تو بے ضابطگیاں سامنے آتی ہیں۔ تنہائی کا جنگل قدرتی طور پر اس کو سنبھالتا ہے۔
# Build a multi-sensor DataFrame
np.random.seed(42)
n = 200
sensor_df = pd.DataFrame({
"voltage_v": 230 + 3 * np.sin(2 * np.pi * np.arange(n) / 24) + np.random.normal(0, 1, n),
"current_a": 15 + 0.8 * np.sin(2 * np.pi * np.arange(n) / 24) + np.random.normal(0, 0.3, n),
"frequency_hz": 50 + np.random.normal(0, 0.05, n),
}, index=pd.date_range("2024-06-01", periods=n, freq="H"))
# Inject a multivariate anomaly — voltage drops, current spikes together
sensor_df.iloc[88, 0] = 194.2 # voltage dip
sensor_df.iloc[88, 1] = 28.7 # current surge (consistent with fault)
clf = IsolationForest(contamination=0.02, random_state=42)
sensor_df["anomaly_score"] = clf.fit_predict(sensor_df[["voltage_v", "current_a", "frequency_hz"]])
anomalies = sensor_df[sensor_df["anomaly_score"] == -1]
print(f"Anomalies detected: {len(anomalies)}")
print(anomalies[["voltage_v", "current_a", "frequency_hz"]].round(2))
حساب کتاب:
Anomalies detected: 4
voltage_v current_a frequency_hz
2024-06-02 07:00:00 234.75 15.84 49.90
2024-06-04 06:00:00 233.09 15.82 50.15
2024-06-04 16:00:00 194.20 28.70 50.08
2024-06-06 05:00:00 235.09 15.41 49.91
عملی طور پر، ڈومین کے لیے مخصوص حد کے اصول آؤٹ لئیر سکور کو ٹریک کرنے کے لیے استعمال کیے جاتے ہیں۔
آؤٹ لیئر ہینڈلنگ
ایک بار باہر جانے والوں کی شناخت ہو جانے کے بعد، ان سے کئی طریقوں سے نمٹا جا سکتا ہے:
-
ہم انتہائی اقدار کو ایک حد تک محدود کرکے Winsorization کا استعمال کرتے ہوئے اس پر پابندی لگاتے ہیں۔
-
اسے انٹرپولیٹڈ یا تخمینہ شدہ قدر سے بدل دیں۔
-
براہ کرم اس پر جھنڈا لگائیں تاکہ ماڈل اسے مناسب طریقے سے سنبھال سکے۔
# Winsorize: cap at the IQR bounds
voltage_winsorized = voltage_clean.clip(lower=lower_bound, upper=upper_bound)
# Replace outliers with time-interpolated values
voltage_outlier_fixed = voltage_clean.copy()
voltage_outlier_fixed[outliers_iqr.index] = np.nan
voltage_outlier_fixed = voltage_outlier_fixed.interpolate(method="time")
print("Outlier treatment comparison:")
for ts in outliers_iqr.index:
print(f"\n {ts}")
print(f" Original: {voltage_clean[ts]:.2f}")
print(f" Winsorized: {voltage_winsorized[ts]:.2f}")
print(f" Interpolated: {voltage_outlier_fixed[ts]:.2f}")
حساب کتاب:
Outlier treatment comparison:
2024-06-04 06:00:00
Original: 312.40
Winsorized: 239.46
Interpolated: 232.01
2024-06-06 10:00:00
Original: 187.20
Winsorized: 220.16
Interpolated: 231.43
Winsorization نقطہ کو برقرار رکھتا ہے لیکن اسے ایک قابل عمل دائرہ کار تک کاٹ دیتا ہے۔ اگر آپ اس معلومات کو برقرار رکھنا چاہتے ہیں کہ کچھ غیر معمولی ہوا ہے تو یہ مفید ہے۔ انٹرپولیشن باہر والوں کے ساتھ ایسا سلوک کرتا ہے جیسے وہ غائب ہوں۔ یہ بہتر ہے اگر آپ کو لگتا ہے کہ ریڈنگ صرف غلط ہیں۔
ڈپلیکیٹس کو کیسے ہٹایا جائے۔
جب ڈیٹا پائپ لائنز کی ناکامی پر دوبارہ کوشش کی جاتی ہے تو ڈپلیکیٹ ٹائم اسٹیمپ عام ہوتے ہیں۔ ٹیبلر ڈپلیکیٹس کے برعکس، ٹائم سیریز ڈپلیکیٹس ہمیشہ ایک جیسے نہیں ہوتے ہیں اور دوبارہ کوششیں ایک ہی ٹائم اسٹیمپ کے لیے قدرے مختلف ریڈنگ دے سکتی ہیں۔
# Inject duplicate timestamps with slightly different values (retry scenario)
dup_index = index.tolist()
dup_index.insert(20, index[20]) # exact duplicate timestamp
dup_index.insert(55, index[55]) # retry duplicate
dup_values = voltage_clean.tolist()
dup_values.insert(20, voltage_clean.iloc[20])
dup_values.insert(55, voltage_clean.iloc[55] + 0.7) # slightly different value
dup_series = pd.Series(dup_values, index=pd.DatetimeIndex(dup_index), name="voltage_v")
print(f"Length with duplicates: {len(dup_series)}")
print(f"Duplicate timestamps: {dup_series.index.duplicated().sum()}")
# Strategy 1: keep first (original reading)
dedup_first = dup_series[~dup_series.index.duplicated(keep="first")]
# Strategy 2: keep mean (average across retries)
dedup_mean = dup_series.groupby(level=0).mean()
print(f"\nAfter dedup (keep first): {len(dedup_first)}")
print(f"After dedup (mean): {len(dedup_mean)}")
# Show the retry duplicate
ts_retry = index[55]
print(f"\nRetry duplicate at {ts_retry}:")
print(f" Values: {dup_series[ts_retry].values.round(3)}")
print(f" Keep first: {dedup_first[ts_retry]:.3f}")
print(f" Mean: {dedup_mean[ts_retry]:.3f}")
حساب کتاب:
Length with duplicates: 170
Duplicate timestamps: 2
After dedup (keep first): 168
After dedup (mean): 168
Retry duplicate at 2024-06-03 07:00:00:
Values: [235.198 234.498]
Keep first: 235.198
Mean: 234.848
زیادہ تر سینسر پائپ لائنوں کے لیے، کیپ فرسٹ درست ڈیفالٹ ہے۔ پہلا پاس اصل پڑھا ہوا ہے۔ اوسط اس وقت معنی خیز ہوتے ہیں جب ایک ہی مقدار کی پیمائش کرنے والے آزاد سینسر سے دوبارہ کوششیں کی جاتی ہیں۔
فریکوئینسی سیدھ اور دوبارہ نمونہ کاری
حقیقی دنیا کی پائپ لائنیں اکثر ڈیٹا کو مختلف فریکوئنسیوں پر ملا دیتی ہیں۔ مثال کے طور پر، آپ کو ایک گھنٹہ کے موسمی فیڈ کے ساتھ ایک منٹ کی میٹر ریڈنگ کی ضرورت ہو سکتی ہے۔ آپ کو تعدد کو یکجا کرنے سے پہلے واضح طور پر ترتیب دینا چاہیے۔
# 1-minute power draw readings
power_1min = pd.Series(
42 + 18 * ((pd.date_range("2024-06-01", periods=1440, freq="T").hour.isin(range(8, 19)))).astype(int)
+ np.random.normal(0, 2, 1440),
index=pd.date_range("2024-06-01", periods=1440, freq="T"),
name="power_kw"
)
# Downsample to hourly: mean is appropriate for power (average over the hour)
power_hourly_mean = power_1min.resample("H").mean().round(2)
# Downsample to hourly: max (peak demand within the hour)
power_hourly_max = power_1min.resample("H").max().round(2)
# Downsample to hourly: sum (total energy = kWh)
energy_hourly_kwh = (power_1min.resample("H").sum() / 60).round(3)
comparison = pd.DataFrame({
"mean_kw": power_hourly_mean,
"peak_kw": power_hourly_max,
"energy_kwh": energy_hourly_kwh,
}).iloc[7:13]
print(comparison)
حساب کتاب:
mean_kw peak_kw energy_kwh
2024-06-01 07:00:00 42.13 46.28 42.133
2024-06-01 08:00:00 60.56 64.81 60.557
2024-06-01 09:00:00 59.91 64.88 59.912
2024-06-01 10:00:00 60.07 65.16 60.066
2024-06-01 11:00:00 60.08 64.99 60.083
2024-06-01 12:00:00 59.72 63.65 59.724
آپ جو مجموعہ منتخب کرتے ہیں وہ بہاو کے استعمال کے لیے بہت اہم ہے۔ اوسط طاقت لوڈ پروفائلنگ کے لیے موزوں ہے۔ صلاحیت کی منصوبہ بندی کے لیے چوٹی کی طاقت مناسب ہے۔ کل (kWh میں تبدیل) بلنگ کے لیے موزوں ہے۔ شاید آپ دیکھ سکتے ہیں کہ کیوں۔ صحیح جواب تکنیکی نہیں ہے، لیکن ڈومین مخصوص ہے۔
ہموار شور
خام سینسر ڈیٹا میں اکثر اعلی تعدد شور ہوتا ہے جو بنیادی سگنل کو دھندلا دیتا ہے۔ فیچر انجینئرنگ سے پہلے ہموار کرنا ماڈل کو مناسب شور سے روکتا ہے، لیکن زیادہ ہموار کرنا حقیقی دنیا کی خرابی کو ختم کر دیتا ہے۔
تیزی سے وزنی حرکت پذیری اوسط
ایکسپونینشیلی ویٹڈ موونگ ایوریج (EWMA) فراہم کرتا ہے: حالیہ مشاہدات کو زیادہ وزن دیں۔ سطح کی تبدیلیوں کے لیے تیزی سے ڈھال لیتا ہے۔ یہ غیر معمولی سگنلز کے لیے ایک سادہ موونگ ایوریج سے بہتر ہے۔
# Noisy temperature sensor (°C)
temp_noisy = pd.Series(
3.5
+ 1.2 * np.sin(2 * np.pi * np.arange(168) / 24)
+ np.random.normal(0, 0.8, 168), # high noise
index=pd.date_range("2024-06-01", periods=168, freq="H"),
name="temperature_c"
)
temp_ewma = temp_noisy.ewm(span=6, adjust=False).mean()
temp_sma = temp_noisy.rolling(window=6, center=True).mean()
comparison = pd.DataFrame({
"raw": temp_noisy,
"ewma": temp_ewma.round(3),
"sma": temp_sma.round(3),
}).iloc[22:30]
print(comparison)
حساب کتاب:
raw ewma sma
2024-06-01 22:00:00 3.212372 2.843 3.035
2024-06-01 23:00:00 3.106840 2.918 3.176
2024-06-02 00:00:00 3.712290 3.145 3.011
2024-06-02 01:00:00 3.344376 3.202 3.294
2024-06-02 02:00:00 2.148946 2.901 3.705
2024-06-02 03:00:00 4.241105 3.284 4.087
2024-06-02 04:00:00 5.677429 3.968 4.381
2024-06-02 05:00:00 5.400083 4.377 4.765
Savitzky-Golay فلٹر
سگنلز کے لیے جہاں چوٹی کی شکلوں کو محض ہموار کرنے کی بجائے محفوظ کرنے کی ضرورت ہوتی ہے، Savitzky-Golay فلٹر سلائیڈنگ ونڈو میں ایک کثیر الثانی کو فٹ کرنے اور حقیقی اسپائک اونچائی کو محفوظ رکھنے میں بہتر ہے۔
from scipy.signal import savgol_filter
temp_savgol = pd.Series(
savgol_filter(temp_noisy.values, window_length=11, polyorder=2),
index=temp_noisy.index,
name="temp_savgol"
).round(3)
print(pd.DataFrame({
"raw": temp_noisy,
"savgol": temp_savgol,
}).iloc[22:30])
حساب کتاب:
raw savgol
2024-06-01 22:00:00 3.212372 2.960
2024-06-01 23:00:00 3.106840 2.944
2024-06-02 00:00:00 3.712290 3.114
2024-06-02 01:00:00 3.344376 3.379
2024-06-02 02:00:00 2.148946 3.809
2024-06-02 03:00:00 4.241105 4.288
2024-06-02 04:00:00 5.677429 4.749
2024-06-02 05:00:00 5.400083 5.138
سکیما اور عقل کی تصدیق
تصدیق کے بغیر صفائی نامکمل ہے۔ آپ کو خودکار جانچ کی ضرورت ہے جو کہ جب بھی نیا ڈیٹا آتا ہے چلتے ہیں۔ آپ کو مسائل تلاش کرنے کی ضرورت ہے اس سے پہلے کہ وہ خود بخود ڈاون اسٹریم ماڈل کو توڑ دیں۔
def validate_time_series(series: pd.Series, config: dict) -> dict:
"""
Run schema and sanity checks on a time series.
Returns a report dict with pass/fail per check.
"""
report = {}
# Frequency check
inferred = pd.infer_freq(series.index)
report["freq_regular"] = inferred == config["expected_freq"]
# Missing value threshold
missing_rate = series.isna().mean()
report["missing_below_threshold"] = missing_rate <= config["max_missing_rate"]
report["missing_rate"] = round(missing_rate, 4)
# Value range check
in_range = series.dropna().between(config["min_value"], config["max_value"])
report["values_in_range"] = in_range.all()
report["out_of_range_count"] = (~in_range).sum()
# Duplicate timestamps
report["no_duplicates"] = not series.index.duplicated().any()
# Monotonic index
report["index_monotonic"] = series.index.is_monotonic_increasing
return report
config = {
"expected_freq": "H",
"max_missing_rate": 0.05,
"min_value": 210.0,
"max_value": 250.0,
}
report = validate_time_series(voltage_outlier_fixed, config)
print("=== VALIDATION REPORT ===")
for check, result in report.items():
if check in ("missing_rate", "out_of_range_count"):
print(f" {check}: {result}")
else:
status = "✓ PASS" if result else "✗ FAIL"
print(f" {status} {check}")
حساب کتاب:
=== VALIDATION REPORT ===
✗ FAIL freq_regular
✓ PASS missing_below_threshold
missing_rate: 0.0
✓ PASS values_in_range
out_of_range_count: 0
✓ PASS no_duplicates
✓ PASS index_monotonic
یہ تصدیق کنندہ ایک قسم کا فنکشن ہے جو پروڈکشن پائپ لائن میں ڈیٹا اکٹھا کرنے کے تمام مراحل کو سمیٹتا ہے۔ اسے صاف کرنے سے پہلے یہ دیکھنے کے لیے چلائیں کہ کیا ٹوٹا ہے، اور صفائی کے بعد یہ دیکھنے کے لیے کہ آیا سب کچھ گزر گیا ہے۔
مکمل صفائی چیک لسٹ
آنے والی ٹائم سیریز ڈیٹاسیٹ پر چلنے کا مکمل سلسلہ درج ذیل ہے:
| قدم | ٹیکنالوجی | کب استعمال کرنا ہے۔ |
|---|---|---|
| آڈٹ | انڈیکس، غائب نقشے، قدر کی حد چیک کریں۔ | ہمیشہ - کسی اور چیز سے پہلے |
| ری انڈیکس | reindex معیاری تعدد کے ساتھ |
اگر کوئی ٹائم اسٹیمپ نہیں ہے تو وہ NaN نہیں ہے۔ |
| غائب: مختصر وقفے | وقت کی مداخلت | مسلسل سگنل، وقفہ ≤ 3 مراحل |
| غائب: سٹیپ سگنل | آگے بھرنا | زمرہ دار یا سیٹ پوائنٹ ڈیٹا |
| غائب: طویل وقفہ | موسمی سڑن کی تبدیلی | موسمی سگنل، وقفہ > 6 قدم |
| بیرونی: غیر متغیر | رولنگ Z-score یا IQR | سنگل سینسر، مقامی بے ضابطگی |
| Outlier: ملٹی ویریٹ | الگ تھلگ جنگل | متعدد متعلقہ سینسر |
| آؤٹ لیئر ہینڈلنگ | Winsorize یا interpolate | اس بات پر منحصر ہے کہ واقعہ حقیقی ہے یا نہیں۔ |
| نقل | پہلے یا گروپ اوسط کو برقرار رکھیں | پائپ لائن ڈپلیکیشن کی دوبارہ کوشش کریں۔ |
| دوبارہ نمونہ لینا | .resample() صحیح گنتی کے ساتھ |
شامل ہونے سے پہلے تعدد کی ترتیب |
| ہموار کرنا | EWMA یا Savitzky-Golay | فنکشنل انجینئرنگ سے پہلے شور سینسر |
| چیک کریں | سکیما + سنٹی چیک | صفائی کے بعد، ہر نئے بیچ |
ختم
آرڈر اہم ہے۔ تبدیل کرنے سے پہلے دوبارہ ترتیب دیں۔ ہموار کرنے سے پہلے متبادل کریں۔ براہ کرم ہر چیز کے بعد تصدیق کریں۔ قدموں کو چھوڑنا یا انہیں غلط ترتیب میں کرنا ان طریقوں سے غلطیاں پیدا کرے گا جن کا پتہ لگانا بہت مشکل ہے ایک بار جب آپ ماڈل کی پیشین گوئیوں کو دیکھیں گے۔
کلیننگ ٹائم سیریز کوئی دلکش کام نہیں ہے، لیکن صاف کیے گئے ڈیٹا اور احتیاط سے ڈیزائن کیے گئے فیچرز پر تربیت یافتہ ماڈلز تقریباً ہمیشہ ہی خراب صاف ڈیٹا پر تربیت یافتہ زیادہ نفیس ماڈلز کو پیچھے چھوڑ دیتے ہیں۔ اس پائپ لائن کو صحیح طریقے سے ترتیب دینا سب سے مؤثر چیز ہے جو آپ ٹائم سیریز کے ڈیٹا پر آسان ترین الگورتھم چلانے سے پہلے کر سکتے ہیں۔