האם מחירי הטיסות הולכים לעלות? פרויקט של דאטה אנליסט – חלק ג' – ניתוח נתונים ושורה תחתונה

לאחר שאספנו את הנתונים בחלק הראשון של המאמר, ועשינו עליהם בדיקות שפיות ואימות בחלקו השני, בחלק הזה כבר נבצע שליפה משמעותית וננתח את הנתונים.

אבל עוד לפני שנצלול פנימה לנתונים – חשוב להבין מה אנחנו בכלל מחפשים.

אז קודם כל – הנה טיפ חשוב:

התובנות הכי מעניינות מגיעות לרוב מהצלבה של נתונים.

ובמקרה של לוח הטיסות – ההצלבה העיקרית האפשרית היא בין הטיסות הנכנסות לטיסות היוצאות.
הצלבה כזו יכולה לתת לנו מידע מאוד מעניין הנוגע לעיכובים בטיסות, ובעיקר ל"זמן הסבב" של חברות תעופה – הזמן בין הנחיתה להמראה לטיסה הבאה.

המדד הזה חשוב במיוחד לחברות לואו קוסט – שהמודל העסקי שלהן מניח שהמטוס נמצא כמה שיותר זמן באוויר – כי רק שם הוא "עושה כסף".

בשביל לחשב את זמן הסבב ישנם מספר שלבים שעלינו לעבור, ואותם כמובן נממש ב-SQL.
אבל עוד לפני שנעשה זאת – צריכים

 

הגדרות והנחות עבודה

בשביל להביא את הנתונים למצב שבו נוכל לחשב מדדים ולנתח אותם, נדרש קודם כל לקבוע הגדרות – כולל 'יישור' של הנתונים.

אז במקרה הזה ישנם כמה היבטים הדורשים התייחסות – כמה מהם דורשים קצת הבנה בעולם התוכן של הטיסות לחו"ל.

יש בגדול שלושה היבטים עיקריים בשלב ההגדרות:

  • אוכלוסייה רלוונטית וסוגי אוכלוסיות
  • מדדים רלוונטים

אוכלוסייה רלוונטית וסוגי אוכלוסיות

מטרת הגדרת האוכלוסייה היא להבין אילו תצפיות (במקרה הזה טיסות) נכנסות לניתוח ואילו לא. בנוסף לכך – נוכל לסווג את האוכלוסיות (טיסות) לסוגים שונים.

קיימות מספר הגדרות אוכלוסייה:

  • הגדרה ראשונה בהקשר הזה תהיה רק טיסות שהמריאו או נחתו בפועל (ערכים 'DEPARTED\LANDED' בשדה CHRMINE).
  • עוד משהו שתמיד ייכנס להגדרות האוכלוסייה הרלוונטית הוא טווח התאריכים הרלוונטי לנתונים. במקרה הזה – נניח שניקח את כלל התאריכים של הטיסות.
    אגב – מאחר ושלפנו את הטבלה מספר פעמים בשביל לקבל מידע היסטורי – חשוב לוודא שכל טיסה בכל תאריך מופיעה פעם אחת בלבד.
  • זיהוי טיסות הלוך-חזור
    זמן סבב מוגדר כזמן בין הנחיתה להמראה עבור הטיסה חזור.
    מאחר וכל רשומה בטבלה מייצגת טיסה אחת בלבד – נדרש כמובן להצליב בין הטיסה הלוך והטיסה חזור.
    איך נזהה קשר בין טיסה הלוך לטיסה חזור?
    בעולם התעופה ישנו כלל מקובל, לפיו עבור חברות זרות (ששדה התעופה אינו הבסיס שלהן) מספר הטיסה חזור שווה למספר הטיסה הלוך ועוד 1.
    כאשר נממש את הכלל הזה – נקבל את הקשר בין טיסה הלוך לטיסה חזור.
    כמובן שהכלל הזה לא יהיה נכון עבור חברות ישראליות, אבל עבורן חישוב זמן הסבב יהיה גם פחות רלוונטי בשדה הבית (נתב"ג).
    לפיכך – נבצע את החישוב רק עבור חברות זרות.
  • המשמעות של 'זמן סבב' היא הזמן על הקרקע בין נחיתה להמראה.
    המדד הזה מאוד חשוב לבחינת יעילות תפעולית – והוא קריטי עבור חברות לואו קוסט.
    במידה ומטוס חונה בשדה התעופה הרבה זמן – לדוגמה אפילו כל הלילה לקראת טיסה חזור – אין משמעות מאוד גבוהה למדד, והוא יהיה כמובן מוטה מעלה.
    זה קורה לא מעט בנתיבים ארוכים, שבהם זמן הטיסה מתואם עם טיסות המשך ביעד, או עם מועדי תחילת יום העסקים (לדוגמה: הטיסות לניו יורק ממריאות מישראל באיזור חצות על מנת להגיע ליעד ממש בתחילת יום העסקים).
    לכן – יש לסנן טיסות עם זמן סבב שיוצא מעל למספר שעות מסוים.
    כמה שעות? את זה כבר צריך לנתח, אבל נניח שעתיים בתור התחלה.

מדדים רלוונטים

נרצה לחשב את זמן הסבב, ואת העיכוב בזמן הסבב הזה.
את כל אחד מהם נחשב פעמיים: פעם אחת עבור המועד המתוכנן ופעם שניה עבור המועד בפועל.
להלן החישובים:

  • עיכוב בזמן הנחיתה = מועד הנחיתה בפועל פחות מועד הנחיתה המתוכנן.
    בבסיס הנתונים זה יהיה מועד ההמראה/נחיתה בפועל (CHPTOL) פחות מועד ההמראה/נחיתה המתוכנן (CHSTOL) עבור טיסות נכנסות (CHAORD="A")
  • עיכוב בזמן ההמראה = מועד ההמראה בפועל פחות מועד הנחיתה המתוכנן.
    בבסיס הנתונים זה יהיה מועד ההמראה/נחיתה בפועל (CHPTOL) פחות מועד ההמראה/נחיתה המתוכנן (CHSTOL) עבור טיסות יוצאות (CHAORD="D")
  • זמן הסבב המתוכנן = מועד העזיבה המתוכנן פחות מועד הנחיתה המתוכנן.
  • זמן הסבב בפועל = מועד העזיבה בפועל פחות מועד הנחיתה בפועל.
  • יחס זמן הסבב בפועל מול המתוכנן = זמן הסבב בפועל לחלק בזמן הסבב המתוכנן, פחות אחד.

 

מימוש בשאילתת SQL

אז לאחר קביעת ההגדרות אנחנו מוכנים למימוש השאילתה!

לצורך הפשטות, את השאילתה נממש תחילה על הטבלה הפשוטה (שליפת הנתונים פעם במועד מסוים).
אז עוד לפני השאילתה, נסדר קצת את הטבלה בצורה נוחה יותר – כולל עדכון מזהה הטיסה לפורמט מספרי:

SELECT distinct
[CHOPER] as airlineCode
,case when(len([CHFLTN])=2) then '00'+CHFLTN
when(len([CHFLTN])=3) then '0'+CHFLTN else CHFLTN end as flightNum
,[CHOPERD] as airlineName
,[CHSTOL] as scheduledTime
,[CHPTOL] as actualTime
,[CHAORD] as flightType
,[CHLOC1] as otherAirportCode
,[CHLOC1D] as otherAirportName
,[CHLOC1T] as otherAirportCity
,[CHLOCCT] as otherAirportCountry
,[CHTERM] as terminal
,[CHCINT] as checkinCounter
,[CHCKZN] as checkinZone
,[CHRMINE] as flightStatus
into flightlistRaw
FROM flightBoard
where CHRMINE in ('LANDED','DEPARTED')
and len(chfltn)<=4
and chfltn not like '%[a-z]%'
and chfltn not like '%[A-Z]%'
and [CHRMINE] in ('DEPARTED','LANDED')
order by CHSTOL, CHLOC1,case when(len([CHFLTN])=2) then '00'+CHFLTN
when(len([CHFLTN])=3) then '0'+CHFLTN else CHFLTN end

 

חשוב לזכור שצריך לסנן מתוך הטבלה טיסות Code Sharing – מה שהראנו בחלק הקודם של המאמר (סינון פרטי הטיסה בעלת מספר הטיסה המינימלי מבין כל טיסה שיוצאת ליעד מסוים בתאריך ושעה מסוימים).
לאחר הסינון הזה – נקבל טבלה מסודרת, שמוכנה לשליפה (נקרא לה flightListFinal).

שאילתת חישוב זמן הסבב

בשלב הזה נפעיל את השאילתה הבאה:

select
inb.airlineCode, inb.airlineName
, avg(datediff(minute,inb.scheduledTime,outb.scheduledTime)) as scheduledCycleTime
, avg(datediff(minute,inb.actualTime,outb.actualTime)) as actualCycleTime
, count(*) as combinations
, count(distinct inb.flightNum+cast(inb.scheduledTime as varchar)) as inbFlights
, count(distinct outb.flightNum+cast(outb.scheduledTime as varchar)) as outbFlights
, avg(DATEDIFF(minute,inb.scheduledTime,inb.actualTime)) as inbFlightDelay
, DATEDIFF(day,min(inb.scheduledTime),max(inb.scheduledTime)) as sampleDays
, avg(datediff(minute,inb.actualTime,outb.actualTime)) –
avg(datediff(minute,inb.scheduledTime,outb.scheduledTime)) as cycleDiff
, cast(avg(datediff(minute,inb.actualTime,outb.actualTime)) as float) /
cast(avg(datediff(minute,inb.scheduledTime,outb.scheduledTime)) as float) -1
as cycleDiffRatio
from flightlistFull inb
join flightlistFull outb
on inb.airlineCode = outb.airlineCode
and cast(inb.flightNum as int) = cast(outb.flightNum as int)-1
and inb.otherAirportCode = outb.otherAirportCode
where inb.flightType = 'A'
and outb.flightType = 'D'
and inb.codeShareFlight = 0
and outb.codeShareFlight = 0
and datediff(minute,inb.actualTime,outb.actualTime) between 0 and 2*60
group by inb.airlineCode, inb.airlineName
having count(*) >= 30
order by
cast(avg(datediff(minute,inb.actualTime,outb.actualTime)) as float) /
cast(avg(datediff(minute,inb.scheduledTime,outb.scheduledTime)) as float) -1
desc

 

אז מה השאילתה הזו עושה?

השלב הראשון כאן הוא הצלבה בין טבלת הטיסות לעצמה – פחות אחת עבור הטיסות הנכנסות, ופעם שניה עבור הטיסות היוצאות.
הקריטריונים בהצלבה:

  • הטבלה השמאלית תהיה הטיסות הנכנסות 'inb.flightType = 'A' .
  • הטבלה הימנית תהיה הטיסות היוצאות 'outb.flightType = 'D .
  • חברת התעופה בשתי הטבלאות תהיה זהה.
  • שדה התעופה האחר בשתי הטבלאות יהיה זהה.
  • מספר הטיסה בטבלה הימנית (טיסות יוצאות) תהיה שווה למספר הטיסה בטבלה השמאלית (טיסות נכנסות) + 1.

השלב השני הוא קיבוץ לפי חברת תעופה – על מנת לבדוק לכל חברה כזו מהו זמן הסבב הממוצע.

בשלב השלישי ניקח רק חברות תעופה שהיו להן יותר מ-30 טיסות בטווח הזמנים הרלוונטי.
מדוע 30? מספר אצבע שמייצג מספר מספיק של תצפיות בשביל שהמדד יהיה מהימן (התכנסות להתפלגות נורמלית למי שבאמת רוצה להתעמק)…

בשלב השלישי – נחשב את המדדים:

  • זמן סבב מתוכנן – scheduledCycleTime
  • זמן סבב בפועל – actualCycleTime
  • מספר צירופי טיסות – combinations
  • מספר טיסות נכנסות – inbFlights
  • מספר טיסות יוצאות – outbFlights
    (כמובן ש-3 המדדים של מספר הטיסות אמורים להיות זהים בשליפה)
  • העיכוב הממוצע בטיסה הנכנסת – inbFlightDelay
  • מספר הימים בהם היו טיסות – sampleDays
  • עיכוב ממוצע בזמן הסבב – cycleDiff
  • אחוז העיכוב הממוצע בזמן הסבב – cycleDiffRatio

 

נו? אז האם מחירי הטיסות הולכים לעלות??

אז התוצאה של השאילתה עבור כחודשיים היא כזו:

ממצאים עיקריים:

  • חברות הלואו קוסט הגדולות (ריאנאייר, וויזאייר ואיזיג'ט) 'מככבות' בראש הרשימה עם עיכוב של כ-70% ומעלה מהזמן המתוכנן של הסבב.
  • זמן הסבב המתוכנן שלהן הוא כ-50-55 דקות, וזמן הסבב בפועל הוא כשעה וחצי.
  • ניתן לראות (נכון גם בחברות עם עיכוב נמוך יותר) שזמן הסבב בפועל הוא יחסית עקבי.

התפלגות העיכוב בזמן הסבב

בשלב השני נראה לשלוף את נתוני הטיסות בפועל עבור חברה מסוימת ולבדוק האם יש לעיכוב בזמן הסבב התפלגות מסוימת. במקרה הזה נסתכל על Ryanair:

העיכוב מתפלג נורמלי עם זנב ימני. הערך השכיח הוא 0.7 (כלומר 70% חריגה מזמן הסבב המתוכנן).
הזנב הימני במקרה הזה מעט פחות משמעותי מאחר וסיננו בשאילתה זמן סבב בפועל של עד שעתיים.

 

מסקנות

אז אמנם גילינו כ-70% עיכוב בזמן הסבב של חברות הלואו קוסט בנתב"ג. אך אולי הוא נובע מעיכוב בטיסה הנכנסת?
מהנתונים עולה שברוב המקרים – במיוחד אצל חברות הלואו קוסט הגדולות – כל העיכוב נובע מהטיסה היוצאת.

יש כנראה משהו בנתב"ג שגורם לעיכובים…

מהן המשמעויות?

אז לפי הממצאים – יש לכאורה עיכובים – ואפילו משמעותיים.

אבל זה לא מספיק עבורנו כאנליסטים רק להציג את המדד ה'יבש' – נדרש מאיתנו להציג גם פרשנות.
במקרה הזה – נרצה גם להבין מהן הסיבות לעיכובים הללו – ובשלב מאוחר יותר כיצד ניתן למזער ואפילו למנוע אותם.

עוד לפני הפרשנות, חשוב להדגיש היבט מסוים בניתוח:
על מנת שהניתוח כאן יהיה שלם ואף תקף – חשוב לבדוק מה קורה בנמלי תעופה אחרים באירופה, מאחר וייתכן שעיכוב של 70% בזמן הסבב הוא דבר מקובל ונורמלי.
מאחר ובשלב הזה אין נתונים זמינים מעין אלה – ניאלץ להתייחס לממצאים ברשות עצמם – וכמובן בעירבון מוגבל.

חיזוק לטענה שזמן הסבב הוא אכן ארוך מהמקובל – מתקבל משלל ידיעות של חברות הלואו קוסט עצמן. הנה שתי דוגמאות:

  • וויזאייר ביקשה מהמדינה להקים בסיס פעילות בנתב"ג מאחר והנתיבים לישראל מצריכים "לוגיסטיקה משמעותית"
  • ריאנאייר ביקשה למנוע מנוסעים בטיסות מ/אל ישראל להעלות מזוודות טרולי למטוס – בתואנה של נהלי אבטחה, בשונה מיתר הטיסות של החברה באירופה.

בהנחה שאכן העיכוב הוא נתון חריג ביחס לנמלי תעופה אחרים, יכולות להיות להם מספר סיבות:

  • יעד אגרסיבי מדי – ייתכן שחברות מסוימות קובעות יעד לא ריאלי לזמן הסבב – ביחס ליכולת של שדה התעופה לשרת אותן.
  • מדידה לא נכונה – ייתכן שקיימת טעות עקבית במדידה של זמני הטיסות בפועל.
    מאחר ומדובר מצד אחד בנתונים שכל חברה מוסרת בכל נמלי התעופה בהן היא פועלת, ומצד שני נתון שהמוני נתונים מסתמכים עליו – ככל הנראה שכאן יש עקביות ברמת הדיוק – לעומת נמלי תעופה אחרים.
  • סיבה תפעולית – היכולת של שדה התעופה לעמוד ביעדים שהגדירו החברות היא לא מלאה.
    מאחר ונמלי תעופה הם מפעל לוגיסטי מורכב למדי – ככל הנראה שכאן נמצא חלק מהותי מהגורמים לעיכוב.
    ייתכן שמדובר בקליטת הנוסעים, טעינת המזוודות, תהליכי הביטחון וכיו"ב.
    לכן, ההמלצה במקרה הזה תהיה להעמיק את הבדיקה לזיהוי הגורמים התפעוליים לעיכובים.

 

השורה התחתונה

אז זיהינו שקיים עיכוב משמעותי לכאורה בזמן הסבב – במיוחד בחברות הלואו קוסט הגדולות.
בהנחה שזהו נתון גבוה ביחס לנמלי תעופה אחרים באירופה (שוב – נדרש לוודא זאת) – זה מצביע על בעיה מסוימת – ככל הנראה תפעולית.
מאחר והממצא עקבי עבור הרבה חברות תעופה (בעיקר חברות לואו קוסט אך לא רק) – כנראה שהוא מצביע על היבט תפעולי הקשור לשדה התעופה עצמו.

חברות מסוימות – כמו ריאנאייר – מחפשות כל העת שדות תעופה מרוחקים יותר – בהם יוכלו לפעול עם מינימום עיכובים וגם מינימום עלויות (אגרות נוסעים).

עבור חברות כאלו – בהן יש תמחור מאוד אגרסיבי לכרטיסי הטיסה – סביר מאוד להניח שהעיכוב המהותי בזמן הסבב ישפיע לרעה עלינו כנוסעים.
זה יכול לקרות או דרך עלייה במחירי הטיסות, או דרך החלטה שהן כלל לא ממשיכות לטוס לשדות כאלה כי זה לא רווחי עבורן או לא מתיישר עם המודל העסקי שלהן (ואז גם תהיה עליה במחירי הטיסות לאור חוסר תחרות).

האם זה בודאות יקרה? כנראה שלא – החיים מורכבים יותר.
האם זה תרחיש סביר? בהחלט כן.

וכאן נמצאת הפואנטה של סדרת המאמרים…

סדרת המאמרים של נתוני הטיסות באה להציג Case Study לניתוח אנליטי – ולהדגיש את תהליך העבודה הנכון.

בניתוח שכזה, ברוב המקרים הנתונים יהיו רק ה'תפאורה', שתביא להמלצה מסוימת.
בהמלצה כזו אין תמיד את כל הנתונים, וכל הסיבות – ולכן גם התוצאה לא תהיה חד משמעית.

ולמה זה קורה?
כי ניתוח אנליטי הוא לא תוצאה חד משמעית, אלא תהליך למידה של תופעה מסוימת והתמודדות איתה.
אז אמנם לעיתים קרובות אין תוצאה חד משמעית…
אבל מה שכן יהיה כמעט תמיד הוא הוא כיוון חשיבה מסוים או השערה מסוימת שנדרש להמשיך לבדוק או לתקף.

זה בדיוק המקרה כאן – יש ממצא יחסית ברור של עיכוב מהותי בטיסות, שקיימת סבירות מסוימת שיביא לעליה במחירי הטיסות.
ואת הממצא הזה נדרש להמשיך לחקור בשביל להבין את ההקשר שלו ולהשפיע עליו.

 

אז כל עוד עדיין יש טיסות זולות לחו"ל – תכינו את הארנקים (זה תמיד נכון…)

 

נתראה ב-Case Study הבא!

 

השארת תגובה