306. MapReduce

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

אמרתי שהפעולה הזאת דומה לפעולה Map Reduce מתכנות פונקציונאלי.

התבלבלתי – הדבר לא נכון והפעם אני אסביר קצת על Map Reduce.

הדרך הפשוטה ביותר להסביר את Map Reduce היא כזאת: נניח שיש לנו אוסף רב של עובדים. על כל עובד אנחנו יודעים מספר פרטים, למשל, את הגיל שלו, את המשכורת שלו, את המחלקה אליה הוא שייך, את מספר הילדים שיש לו וכו’.

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

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

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

איך כותבים דבר כזה?

מתבקש להשתמש בGroup By (ראו טיפ מספר 115-116) ובSelect (ראו טיפ מספר 92).

הדבר נראה בערך ככה:

1
2
3
4
5
6
7
8
9
10
11
12
public static IDictionary<TPivot, TResult> MapReduce<TSource, TPivot, TResult>
(this IEnumerable<TSource> source,
Func<TSource, TPivot> groupSelector,
Func<IGrouping<TPivot, TSource>, TResult> valueSelector)
{
Dictionary<TPivot, TResult> result =
source.GroupBy(x => groupSelector(x))
.Select(x => new {Key = x.Key, Value = valueSelector(x)})
.ToDictionary(x => x.Key, x => x.Value);
return result;
}

אפשר לקרוא לזה ככה למשל:

1
2
3
IDictionary<string, double> departmentsToAverageSalary =
employers.MapReduce(employer => employer.Department,
department => department.Average(employer => employer.Salary));

מה זה נותן לנו?

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

המשך יום ממפה ומנפה טוב,

(וחג שמח!)

שתף