هذا العمل هو تلخيص وترجمه للباب الثاني (Wrangle) من كتاب R for Data Science للكاتبين Hadly Wickham و Garrett Grolemund مع بعض الاضافات
ترتيب المقاله كالتالي
1- Tibbles.
2- Data import.
3- Tidy data.
تحتاج في البداية ان تقوم بتحميل مكتبة tidyverse
في هذا القسم راح تتعلم
1- الفرق بين tibble و data.fram
2- إنشاء جدول جديد
3- طباعة الجدول
4- Subsetting
خلال هذة المقاله سوف نستعمل ()tibble بدلا هن الدالة التقليدية ()data.frame
للتحويل من ()data.frame الى ()tibbles نستخدم
## # A tibble: 150 x 5
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## <dbl> <dbl> <dbl> <dbl> <fct>
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
## 7 4.6 3.4 1.4 0.3 setosa
## 8 5 3.4 1.5 0.2 setosa
## 9 4.4 2.9 1.4 0.2 setosa
## 10 4.9 3.1 1.5 0.1 setosa
## # … with 140 more rows
ملاحظه: عند استخدام عند استعمالك ()readr::read_csv فإن الناتج يكون tibble لاكن عند استعمال ()base::read.csv فإن الناتج يكون data.frame
data.frame هي الصيغة الاساس لحفظ الجداول في R. مؤخرا قام كل من Kirill Müller و Hadley Wickham بتعديل واضافة بعض المزايا الجديدة الى data.frame واطلقو عليه اسم tibble
اذا tibble ليس الا تحديث ل data.frame
## Parsed with column specification:
## cols(
## id = col_double(),
## Gender = col_character(),
## Age = col_double(),
## `Education level` = col_character(),
## `Marital status` = col_character(),
## City = col_character(),
## `Family member` = col_double(),
## Salary = col_double(),
## `Monthly rent` = col_double()
## )
لاحظ ان ()read_csv من tibble أظهرت لنا type كل عمود بينما ()read.csv من base R لم تظهر لنا شيء
## Rows: 200
## Columns: 9
## $ id <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, …
## $ Gender <chr> "Female", "Female", "Female", "Female", "Female", "…
## $ Age <dbl> 32, 33, 34, 32, 27, 41, 32, 25, 33, 28, 28, 35, 30,…
## $ `Education level` <chr> "B.Sc", "B.Sc", "B.Sc", "B.Sc", "B.Sc", "PhD", "B.S…
## $ `Marital status` <chr> "Single", "Married", "Married", "Married", "Married…
## $ City <chr> "Riyadh", "Riyadh", "Jeddah", "Dammam", "Dammam", "…
## $ `Family member` <dbl> 1, 4, 3, 5, 4, 2, 6, 3, 3, 2, 3, 1, 5, 1, 2, 1, 4, …
## $ Salary <dbl> 12645, 12448, 12391, 8863, 12863, 22334, 11787, NA,…
## $ `Monthly rent` <dbl> 2528, 2384, 2059, 1848, 2526, 4112, 1989, 1899, 242…
## Rows: 200
## Columns: 9
## $ id <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16…
## $ Gender <fct> Female, Female, Female, Female, Female, Female, Male,…
## $ Age <int> 32, 33, 34, 32, 27, 41, 32, 25, 33, 28, 28, 35, 30, 2…
## $ Education.level <fct> B.Sc, B.Sc, B.Sc, B.Sc, B.Sc, PhD, B.Sc, B.Sc, B.Sc, …
## $ Marital.status <fct> Single, Married, Married, Married, Married, Married, …
## $ City <fct> Riyadh, Riyadh, Jeddah, Dammam, Dammam, Riyadh, Jedda…
## $ Family.member <int> 1, 4, 3, 5, 4, 2, 6, 3, 3, 2, 3, 1, 5, 1, 2, 1, 4, 4,…
## $ Salary <int> 12645, 12448, 12391, 8863, 12863, 22334, 11787, NA, 1…
## $ Monthly.rent <int> 2528, 2384, 2059, 1848, 2526, 4112, 1989, 1899, 2423,…
لاحظ ايضا اختلاف ال type بين المتغيرات الاسمية والرقمية
[[]] مع tibble و data.fram## [1] "integer"
## [1] "tbl_df" "tbl" "data.frame"
## [1] "numeric"
استخراج عمود من data.frame يعطينا دائما vector بينما tibble يعطينا دائما tibble
تخيل لسبب ما انك تريد اضافه معلمات اضفية لكل صف. مثلا لنفرض انك قمت ب اختبار علاج على ثلاث مجموعات من الناس
وتريد حفظ بيانات كل مجموعة في صف
a = tibble(age = sample(20:50, 5),
name = letters[1:5])
b = tibble(age = sample(20:50, 5),
name = letters[6:10])
c = tibble(age = sample(20:50, 5),
name = letters[11:15])
tibble(group = c("a","b","c"), data= list(a,b,c))## # A tibble: 3 x 2
## group data
## <chr> <list>
## 1 a <tibble [5 × 2]>
## 2 b <tibble [5 × 2]>
## 3 c <tibble [5 × 2]>
## Error in data.frame(group = c("a", "b", "c"), data = list(a, b, c)): arguments imply differing number of rows: 3, 5
لاحظ انه مع tibble نستطيع اضافة list في خلية لاكن لانستطيع عمل نفس الشيء مع data.fram
ربما يكون هذا مفيدا اذا اردنا عمل model لكل مدينة في هذا الجدول مثلا
## # A tibble: 200 x 9
## id Gender Age `Education leve… `Marital status` City `Family member`
## <dbl> <chr> <dbl> <chr> <chr> <chr> <dbl>
## 1 1 Female 32 B.Sc Single Riya… 1
## 2 2 Female 33 B.Sc Married Riya… 4
## 3 3 Female 34 B.Sc Married Jedd… 3
## 4 4 Female 32 B.Sc Married Damm… 5
## 5 5 Female 27 B.Sc Married Damm… 4
## 6 6 Female 41 PhD Married Riya… 2
## 7 7 Male 32 B.Sc Married Jedd… 6
## 8 8 Female 25 B.Sc Married Riya… 3
## 9 9 Male 33 B.Sc Married Riya… 3
## 10 10 Female 28 M.Sc Married Jedd… 2
## # … with 190 more rows, and 2 more variables: Salary <dbl>, `Monthly
## # rent` <dbl>
data_14_tibble_by_city<-data_14_tibble %>%
na.omit() %>%
select(-id) %>%
group_by(City) %>%
nest()
data_14_tibble_by_city## # A tibble: 3 x 2
## # Groups: City [3]
## City data
## <chr> <list>
## 1 Riyadh <tibble [110 × 7]>
## 2 Jeddah <tibble [44 × 7]>
## 3 Dammam <tibble [41 × 7]>
# function لحساب الانحدار الخطي
data_lm <- function(data){
lm(`Monthly rent` ~ ., data = data)
}
data_14_tibble_by_city<-data_14_tibble_by_city %>%
mutate(model = map(data, data_lm))
data_14_tibble_by_city## # A tibble: 3 x 3
## # Groups: City [3]
## City data model
## <chr> <list> <list>
## 1 Riyadh <tibble [110 × 7]> <lm>
## 2 Jeddah <tibble [44 × 7]> <lm>
## 3 Dammam <tibble [41 × 7]> <lm>
## [[1]]
##
## Call:
## lm(formula = `Monthly rent` ~ ., data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -230.62 -66.47 2.82 59.05 263.08
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -7.066e+02 8.443e+01 -8.369 3.22e-13 ***
## GenderMale 3.178e+01 1.752e+01 1.814 0.0726 .
## Age 1.167e+01 2.836e+00 4.115 7.86e-05 ***
## `Education level`M.Sc -2.640e+02 2.621e+01 -10.072 < 2e-16 ***
## `Education level`PhD -8.119e+02 4.776e+01 -17.000 < 2e-16 ***
## `Marital status`Single 6.354e+00 2.962e+01 0.215 0.8306
## `Family member` 9.038e+00 7.152e+00 1.264 0.2092
## Salary 2.224e-01 1.585e-03 140.318 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 90.03 on 102 degrees of freedom
## Multiple R-squared: 0.9975, Adjusted R-squared: 0.9973
## F-statistic: 5712 on 7 and 102 DF, p-value: < 2.2e-16
##
##
## [[2]]
##
## Call:
## lm(formula = `Monthly rent` ~ ., data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.51244 -0.22112 -0.00874 0.23636 0.50992
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 8.055e-01 4.855e-01 1.659 0.106
## GenderMale -6.668e-02 9.754e-02 -0.684 0.499
## Age 4.987e+00 1.356e-02 367.860 <2e-16 ***
## `Education level`M.Sc -4.471e-03 1.589e-01 -0.028 0.978
## `Education level`PhD 7.888e-02 2.902e-01 0.272 0.787
## `Marital status`Single 8.546e-02 1.602e-01 0.533 0.597
## `Family member` 1.003e+01 4.409e-02 227.552 <2e-16 ***
## Salary 1.500e-01 1.807e-05 8301.987 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.3084 on 36 degrees of freedom
## Multiple R-squared: 1, Adjusted R-squared: 1
## F-statistic: 4.114e+07 on 7 and 36 DF, p-value: < 2.2e-16
##
##
## [[3]]
##
## Call:
## lm(formula = `Monthly rent` ~ ., data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.5433 -0.2215 0.0000 0.1703 0.4576
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 9.242e-01 6.036e-01 1.531 0.135
## GenderMale -2.882e-02 1.164e-01 -0.248 0.806
## Age 5.998e+00 1.803e-02 332.629 <2e-16 ***
## `Education level`M.Sc -3.317e-02 2.057e-01 -0.161 0.873
## `Education level`PhD 2.348e-01 4.980e-01 0.471 0.640
## `Marital status`Single 7.266e-02 1.685e-01 0.431 0.669
## `Family member` 1.199e+01 4.373e-02 274.176 <2e-16 ***
## Salary 1.800e-01 3.023e-05 5953.170 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.3032 on 33 degrees of freedom
## Multiple R-squared: 1, Adjusted R-squared: 1
## F-statistic: 1.76e+07 on 7 and 33 DF, p-value: < 2.2e-16
Education level طلع مؤثر في البيانات تبع الرياض دون باقي المدن
يمكنك انشاء جدول عن طريق ()tibble
## # A tibble: 5 x 3
## x y z
## <int> <dbl> <dbl>
## 1 1 1 2
## 2 2 1 5
## 3 3 1 10
## 4 4 1 17
## 5 5 1 26
لاحظ ان ()tibble تتكفل بإكمال القيم في حاله ان قيمه الادخال كانت رقم واحد
.ايضا لاحظ انه بمكاننا استخدام المتغيرات التي قمنا بانشائها
بامكانك استخدام ()View وهي built-in from Rstudio
او ()print
( n جرب قيم اخرى مع )
لاستخراج متغير من (عمود) من الجدول استخدم $ او ]]
## [1] 0.7502283 0.4065100 0.5655887 0.6851015 0.2545765
## [1] 0.7502283 0.4065100 0.5655887 0.6851015 0.2545765
## [1] 0.7502283 0.4065100 0.5655887 0.6851015 0.2545765
او اذا اردت يمكنك استخدام ال pipe (%>%) لاكن يجب عليك استخدام .
## [1] 0.7502283 0.4065100 0.5655887 0.6851015 0.2545765
## [1] 0.7502283 0.4065100 0.5655887 0.6851015 0.2545765
%>%تم تطوير pipe عن طريق Stefan Milton Bache في مكتبة magrittr لاكن تم تضمينه مع tidyverse
الهدف منه هو مساعدتك في كتابة الكود بطريقة يسهل قرائتها
من شروط ال pipe ان ياخذ اول argument جدول او vector
لاكن بسبب ان هناك الكثير من ال functions لاتاخذ الجدول او ال vector كأول argumnet نستخدم النقطه التي تساعدنا في التحكم بال pipe.
## # A tibble: 5 x 2
## x y
## <dbl> <dbl>
## 1 0.750 0.738
## 2 0.407 -1.95
## 3 0.566 -0.737
## 4 0.685 -1.19
## 5 0.255 -1.52
## # A tibble: 5 x 2
## x y
## <dbl> <dbl>
## 1 0.750 0.738
## 2 0.407 -1.95
## 3 0.566 -0.737
## 4 0.685 -1.19
## 5 0.255 -1.52
## Error in as.data.frame.default(data): cannot coerce class '"formula"' to a data.frame
##
## Call:
## lm(formula = x ~ y, data = .)
##
## Coefficients:
## (Intercept) y
## 0.6678 0.1455
في هذا القسم راح تتعلم كيف تقراء الملفات بمختلف الصيغ في R
()read_csv يقراء الملفات التي يكون الفاصل بين كل عمود فيها فاصله “،”
()read_csv2 يقرا الملفات التي يكون الفاصل بين كل عمود فيها علامه الفاصلة المنقوطه “;”
()read_tsv يقراء الملفات التي يكون الفاصل بين كل عمود فيها tap ,
()read_table يقرا الملفات التي يكون الفاصل بين كل عمود فيها مسافه ,
()read_fwf + يقرا الملفات التي تكون فيها المسافه بين كل عمود غير محددة وتختلف من عمود لآخر ,
()read_log تقرا ملفات Apach style log ,
كل ماتحتاجة لقراءة اي ملف هو تحديد مكان الملف.
.بالامكان ايضا قراءه المدخلات مباشره
## # A tibble: 2 x 3
## a b c
## <dbl> <dbl> <dbl>
## 1 1 2 3
## 2 4 5 6
في الحالتين السابقتين ()read_csv تستخدم الصف الاول كاسماء للاعمده لاكن احيانا قد لايكون الصف الاول مهما
لنتجاوز السطر غير المرغوب نضيف skip
## # A tibble: 1 x 3
## x y z
## <dbl> <dbl> <dbl>
## 1 1 2 3
او اذا كان الملف يحتوي علي سطر يبداء مثلا بعلامه مثل الهاشتاق نضيف commentثم نحدد نوع العلامه
## # A tibble: 1 x 3
## x y z
## <dbl> <dbl> <dbl>
## 1 1 2 3
احيانا قد لايحتوي الملف على اسماء للاعمده(المتغيرات) في هذه الحاله نحتاج لاضافة الاسماء يديويا باستخدام colnames
## # A tibble: 2 x 3
## x y z
## <dbl> <dbl> <dbl>
## 1 1 2 3
## 2 4 5 6
بعض البيانات تستخدم . او_ للتعبير عن القيم المفقوده (Missing value)
هذه القيم لن تظهر كقيم مفقوده اذا تم قرائتها بالشكل الحالي لذالك سوف نحولها الي NA
## # A tibble: 1 x 3
## a b c
## <dbl> <dbl> <lgl>
## 1 1 2 NA
()*_parseربما ايضا تواجة ايضا مشكلة ان العمود الذي يمثل المبالغ يحتوي علامة على “$” او انه يستعمل نقطه بدل الفاصله لتقسيم الارقام الكبيره
مثلا
1- $5,000,000
2- $5.000.000
3- 5000000
## [1] 100
## [1] 20
## [1] 123.45
## [1] 123456789
## [1] 123456789
## [1] 123456789
او اذا كانت القيمه التي تريدها اتحتوي على كسر
## [1] 1.23
## [1] 1.23
هناك ايضا
1- ()parse_date
2- ()parse_factor
3- ()parse_datetime
4- ()parse_logical
5- ()parse_vectr
6- ()parsee_character
7- ()parsee_time
8-()parsee_guess
()read_csv تقوم بقراءة اول 1000 صف ثم من اجل اختيار type العمود تقوم بتطبيق الشروط التالية عليه
logical : اذا احتوى على “T”, “F” , “TRUE” or “FALSE”.
integer: اذا احتوى على ارقام و علامة “-”.
double: اذا احتوى على كسور مثل “1.1”.
number: اذا احتوى على double مع علامة ال grouping mark.
time: يطابق ال default time_format.
date: يطابق ال default date_format.
date-time: اي قيمه من قيم ال ISO8601 date
تخيل الان لو ان لديك جدول يحتوي على عشرة الاف مشاهده. وان اول 1000 مشاهدة من العمود y الذي يمثل التاريخ مفقودة
كيف سيقراء R هذا الجدول
الجدول challenge يحتوي على هذة المشكلة
الان لنقم بقراءة الجدول
## Parsed with column specification:
## cols(
## x = col_double(),
## y = col_logical()
## )
## Warning: 1000 parsing failures.
## row col expected actual file
## 1001 y 1/0/T/F/TRUE/FALSE 2015-01-16 '/Library/Frameworks/R.framework/Versions/3.6/Resources/library/readr/extdata/challenge.csv'
## 1002 y 1/0/T/F/TRUE/FALSE 2018-05-18 '/Library/Frameworks/R.framework/Versions/3.6/Resources/library/readr/extdata/challenge.csv'
## 1003 y 1/0/T/F/TRUE/FALSE 2015-09-05 '/Library/Frameworks/R.framework/Versions/3.6/Resources/library/readr/extdata/challenge.csv'
## 1004 y 1/0/T/F/TRUE/FALSE 2012-11-28 '/Library/Frameworks/R.framework/Versions/3.6/Resources/library/readr/extdata/challenge.csv'
## 1005 y 1/0/T/F/TRUE/FALSE 2020-01-13 '/Library/Frameworks/R.framework/Versions/3.6/Resources/library/readr/extdata/challenge.csv'
## .... ... .................. .......... ............................................................................................
## See problems(...) for more details.
تجاهل ()readr_example
لاحظ هنا الرساله Warning: 1000 parsing failures.
حتي ناخذ تصور افضل عن الخطاء سوف نستخدم problems()
## # A tibble: 1,000 x 5
## row col expected actual file
## <int> <chr> <chr> <chr> <chr>
## 1 1001 y 1/0/T/F/TRUE/… 2015-01… '/Library/Frameworks/R.framework/Version…
## 2 1002 y 1/0/T/F/TRUE/… 2018-05… '/Library/Frameworks/R.framework/Version…
## 3 1003 y 1/0/T/F/TRUE/… 2015-09… '/Library/Frameworks/R.framework/Version…
## 4 1004 y 1/0/T/F/TRUE/… 2012-11… '/Library/Frameworks/R.framework/Version…
## 5 1005 y 1/0/T/F/TRUE/… 2020-01… '/Library/Frameworks/R.framework/Version…
## 6 1006 y 1/0/T/F/TRUE/… 2016-04… '/Library/Frameworks/R.framework/Version…
## 7 1007 y 1/0/T/F/TRUE/… 2011-05… '/Library/Frameworks/R.framework/Version…
## 8 1008 y 1/0/T/F/TRUE/… 2020-07… '/Library/Frameworks/R.framework/Version…
## 9 1009 y 1/0/T/F/TRUE/… 2011-04… '/Library/Frameworks/R.framework/Version…
## 10 1010 y 1/0/T/F/TRUE/… 2010-05… '/Library/Frameworks/R.framework/Version…
## # … with 990 more rows
نلاحظ هنا الاخطاء موجوده في العمود y حيث ان القيمه المتوقعة كانت Logial لاكن القيمه الفعليه Date
الان ()read_csv استبعد التاريخ ووضع جميع القيم من 1 الى 2000 ك قيم مفقودة
والسبب هو ان كما ذكرنا ان اول 1000 صف يحتوي على Missing values
ابسط طريقه لعلاج المشكله هي بزياده عدد الصفوف التي يعتمدها ()read_csv لتحديد ال type
## Parsed with column specification:
## cols(
## x = col_double(),
## y = col_date(format = "")
## )
الان تم حل المشكلة
لحفظ الملف السابق
.لاكن لاحظ انه عند قرائته مره اخرى سنقع في نفس المشكلة السابقه
## # A tibble: 2,000 x 2
## x y
## <dbl> <lgl>
## 1 404 NA
## 2 4172 NA
## 3 3004 NA
## 4 787 NA
## 5 37 NA
## 6 2332 NA
## 7 2489 NA
## 8 1449 NA
## 9 3665 NA
## 10 3863 NA
## # … with 1,990 more rows
## Parsed with column specification:
## cols(
## x = col_double(),
## y = col_logical()
## )
## # A tibble: 2,000 x 2
## x y
## <dbl> <lgl>
## 1 404 NA
## 2 4172 NA
## 3 3004 NA
## 4 787 NA
## 5 37 NA
## 6 2332 NA
## 7 2489 NA
## 8 1449 NA
## 9 3665 NA
## 10 3863 NA
## # … with 1,990 more rows
والسبب هو ملفات csv لا تحتفظ ب type العمود
لاكن يمكننا استخدام
## # A tibble: 2,000 x 2
## x y
## <dbl> <date>
## 1 404 NA
## 2 4172 NA
## 3 3004 NA
## 4 787 NA
## 5 37 NA
## 6 2332 NA
## 7 2489 NA
## 8 1449 NA
## 9 3665 NA
## 10 3863 NA
## # … with 1,990 more rows
rds هي صيغه خاصه لحفظ البيانات في R
انظرا ايضا
1- ()write_excel_csv لحفظ البيانات بصيغة اكسل
2- ()write_tsv,
haven: يقراء ملفات SPSS, Stata, and SAS
readxl: يقراء ملفات اكسل ( .xls and .xlsx).
DBI: يقراء الجداول من قواعد البيانات
الجداول التاليه كلها تحتي على نفس البيانات لاكن طريقة العرض تختلف من جدول لآخر
المتغيرات هنا هي country, year, cases and population
## # A tibble: 6 x 4
## country year cases population
## <chr> <int> <int> <int>
## 1 Afghanistan 1999 745 19987071
## 2 Afghanistan 2000 2666 20595360
## 3 Brazil 1999 37737 172006362
## 4 Brazil 2000 80488 174504898
## 5 China 1999 212258 1272915272
## 6 China 2000 213766 1280428583
## # A tibble: 12 x 4
## country year type count
## <chr> <int> <chr> <int>
## 1 Afghanistan 1999 cases 745
## 2 Afghanistan 1999 population 19987071
## 3 Afghanistan 2000 cases 2666
## 4 Afghanistan 2000 population 20595360
## 5 Brazil 1999 cases 37737
## 6 Brazil 1999 population 172006362
## 7 Brazil 2000 cases 80488
## 8 Brazil 2000 population 174504898
## 9 China 1999 cases 212258
## 10 China 1999 population 1272915272
## 11 China 2000 cases 213766
## 12 China 2000 population 1280428583
## # A tibble: 6 x 3
## country year rate
## * <chr> <int> <chr>
## 1 Afghanistan 1999 745/19987071
## 2 Afghanistan 2000 2666/20595360
## 3 Brazil 1999 37737/172006362
## 4 Brazil 2000 80488/174504898
## 5 China 1999 212258/1272915272
## 6 China 2000 213766/1280428583
## # A tibble: 3 x 3
## country `1999` `2000`
## * <chr> <int> <int>
## 1 Afghanistan 745 2666
## 2 Brazil 37737 80488
## 3 China 212258 213766
## # A tibble: 3 x 3
## country `1999` `2000`
## * <chr> <int> <int>
## 1 Afghanistan 19987071 20595360
## 2 Brazil 172006362 174504898
## 3 China 1272915272 1280428583
فقط واحد من هذي الجداول هو الاصلح للاستخدام مع tidyverse ونطلق علية tidy dataset
شروط ال Tidy dataset هي كالتالي :
1- كل عمود يمثل متغير
2- كل صف يمثل مشاهدة
3- كل قيمه تكون في خليه
طبعا من الواضح ان table1 هو الوحيد الذي تنطبق علية الشروط.
الهدف من ال Tidy data هو انها تمكننا من استخدام Tidyverse بالطريقة الامثل
على سبيل المثال
# Compute rate per 10,000
# حساب معدل الاصابات لكل 10 الاف شخص
table1 %>%
mutate(rate = cases / population * 10000)## # A tibble: 6 x 5
## country year cases population rate
## <chr> <int> <int> <int> <dbl>
## 1 Afghanistan 1999 745 19987071 0.373
## 2 Afghanistan 2000 2666 20595360 1.29
## 3 Brazil 1999 37737 172006362 2.19
## 4 Brazil 2000 80488 174504898 4.61
## 5 China 1999 212258 1272915272 1.67
## 6 China 2000 213766 1280428583 1.67
## # A tibble: 2 x 2
## year n
## <int> <int>
## 1 1999 250740
## 2 2000 296920
# Visualise changes over time
# تصوير التغير خلال السنوات
library(ggplot2)
ggplot(table1, aes(year, cases)) +
geom_line(aes(group = country), colour = "grey50") +
geom_point(aes(colour = country))احيانا البيانات لاتكون في شكل ال Tidy data لذلك هناك مجموعة من ال , functions التي تساعدنا في تحويلها
هناك نوعين من المشاكل الشائعة في جداول البيانات هما ان يكون هناك
1- متغير واحد مقسم على اكثر من عمود
2- اكثر من متغير في عمود واحد
متغير واحد مقسم على اكثر من عمود
## # A tibble: 3 x 3
## country `1999` `2000`
## * <chr> <int> <int>
## 1 Afghanistan 745 2666
## 2 Brazil 37737 80488
## 3 China 212258 213766
1- لاحظ ان year بدل ان تكون في عمود واحد توزعت على عمودين
2- لاحظ ان cases لم يعد لها عمود
الان سوف نقوم بوضع عمود خاص لكل من year and cases
## # A tibble: 6 x 3
## country year cases
## <chr> <chr> <int>
## 1 Afghanistan 1999 745
## 2 Afghanistan 2000 2666
## 3 Brazil 1999 37737
## 4 Brazil 2000 80488
## 5 China 1999 212258
## 6 China 2000 213766
بامكانك اختيار اي اسم تريد بدل cases او year
استخدام ' حول 1999 و 2000 هو بسبب انها تبدا برقم وليس اسم. انت لست بحاجة اليها اذا كان العمود يبدا بحرف.
اكثر من متغير في عمود واحد
## # A tibble: 12 x 4
## country year type count
## <chr> <int> <chr> <int>
## 1 Afghanistan 1999 cases 745
## 2 Afghanistan 1999 population 19987071
## 3 Afghanistan 2000 cases 2666
## 4 Afghanistan 2000 population 20595360
## 5 Brazil 1999 cases 37737
## 6 Brazil 1999 population 172006362
## 7 Brazil 2000 cases 80488
## 8 Brazil 2000 population 174504898
## 9 China 1999 cases 212258
## 10 China 1999 population 1272915272
## 11 China 2000 cases 213766
## 12 China 2000 population 1280428583
لاحظ ان المتغيرين population و cases مدموجين في عمود واحد
لاحظ ايضا ان
1- العمود الذي يحتوي على اسماء المتغيرات اسمه type
2- العمود الذي يحتوي على القيم اسمه count
## # A tibble: 6 x 4
## country year cases population
## <chr> <int> <int> <int>
## 1 Afghanistan 1999 745 19987071
## 2 Afghanistan 2000 2666 20595360
## 3 Brazil 1999 37737 172006362
## 4 Brazil 2000 80488 174504898
## 5 China 1999 212258 1272915272
## 6 China 2000 213766 1280428583
لاحظ الجدول التالي
## # A tibble: 6 x 3
## country year rate
## * <chr> <int> <chr>
## 1 Afghanistan 1999 745/19987071
## 2 Afghanistan 2000 2666/20595360
## 3 Brazil 1999 37737/172006362
## 4 Brazil 2000 80488/174504898
## 5 China 1999 212258/1272915272
## 6 China 2000 213766/1280428583
العمود rate يحتوي على المتغيرين population و cases ويفصل بينهم /
يمكنك استخدام sep مع regular expression
احيانا قد تحتاج الى تحويل type العمود
## # A tibble: 6 x 4
## country year cases population
## <chr> <int> <int> <int>
## 1 Afghanistan 1999 745 19987071
## 2 Afghanistan 2000 2666 20595360
## 3 Brazil 1999 37737 172006362
## 4 Brazil 2000 80488 174504898
## 5 China 1999 212258 1272915272
## 6 China 2000 213766 1280428583
اذا كانت قيمة sep عدد صحيح فإن ()separate سوف تقسم العمود حسب العدد
## # A tibble: 6 x 4
## country century year rate
## <chr> <chr> <chr> <chr>
## 1 Afghanistan 19 99 745/19987071
## 2 Afghanistan 20 00 2666/20595360
## 3 Brazil 19 99 37737/172006362
## 4 Brazil 20 00 80488/174504898
## 5 China 19 99 212258/1272915272
## 6 China 20 00 213766/1280428583
تقوم بعملية عكسية لما تقوم به ()separate
## # A tibble: 6 x 3
## country new rate
## <chr> <chr> <chr>
## 1 Afghanistan 19_99 745/19987071
## 2 Afghanistan 20_00 2666/20595360
## 3 Brazil 19_99 37737/172006362
## 4 Brazil 20_00 80488/174504898
## 5 China 19_99 212258/1272915272
## 6 China 20_00 213766/1280428583
او
## # A tibble: 6 x 3
## country new rate
## <chr> <chr> <chr>
## 1 Afghanistan 1999 745/19987071
## 2 Afghanistan 2000 2666/20595360
## 3 Brazil 1999 37737/172006362
## 4 Brazil 2000 80488/174504898
## 5 China 1999 212258/1272915272
## 6 China 2000 213766/1280428583
قيمه ال default هي _
هناك نوعين من القيم المفقوده
1- Explicitly : تحمل القيمه NA وتكون موجوده في البيانات
2- Implicitly: لاتكون موجوده في البيانات
.شاهد هذه الجول لتتتضح الصوره افضل
stocks <- tibble(
year = c(2015, 2015, 2015, 2015, 2016, 2016, 2016),
qtr = c( 1, 2, 3, 4, 2, 3, 4),
return = c(1.88, 0.59, 0.35, NA, 0.92, 0.17, 2.66)
)1- ال return عند year == 15و qtr == 4 قيمته هي NA اذا هي Explicitly missing
2- ال return عند year == 16و qtr == 1 قيمته غير موجودة اذا هي Implicitly missing
## # A tibble: 7 x 3
## year qtr return
## <dbl> <dbl> <dbl>
## 1 2015 1 1.88
## 2 2015 2 0.59
## 3 2015 3 0.35
## 4 2015 4 NA
## 5 2016 2 0.92
## 6 2016 3 0.17
## 7 2016 4 2.66
للتحويل من Implicitly missing الي Explicitly missing
ملاحظه: التحويل هنا يكون مفيدا غالبا مع time series analysis
## # A tibble: 8 x 3
## year qtr return
## <dbl> <dbl> <dbl>
## 1 2015 1 1.88
## 2 2015 2 0.59
## 3 2015 3 0.35
## 4 2015 4 NA
## 5 2016 1 NA
## 6 2016 2 0.92
## 7 2016 3 0.17
## 8 2016 4 2.66
والعكس يكون
## # A tibble: 6 x 3
## year qtr return
## <dbl> <dbl> <dbl>
## 1 2015 1 1.88
## 2 2015 2 0.59
## 3 2015 3 0.35
## 4 2016 2 0.92
## 5 2016 3 0.17
## 6 2016 4 2.66
مثال اخر
treatment <- tribble(
~ person, ~ treatment, ~response,
"Derrick Whitmore", 1, 7,
NA, 2, 10,
NA, 3, 9,
"Katherine Burke", 1, 4
)يمكنك تغطية القيم المفقوده بقيمه الصف السابق او اللاحق
## # A tibble: 4 x 3
## person treatment response
## <chr> <dbl> <dbl>
## 1 Derrick Whitmore 1 7
## 2 Derrick Whitmore 2 10
## 3 Derrick Whitmore 3 9
## 4 Katherine Burke 1 4
## # A tibble: 4 x 3
## person treatment response
## <chr> <dbl> <dbl>
## 1 Derrick Whitmore 1 7
## 2 Katherine Burke 2 10
## 3 Katherine Burke 3 9
## 4 Katherine Burke 1 4
الجدول who ليس Tidy حاول تحويله الى Tidy
## # A tibble: 7,240 x 60
## country iso2 iso3 year new_sp_m014 new_sp_m1524 new_sp_m2534 new_sp_m3544
## <chr> <chr> <chr> <int> <int> <int> <int> <int>
## 1 Afghan… AF AFG 1980 NA NA NA NA
## 2 Afghan… AF AFG 1981 NA NA NA NA
## 3 Afghan… AF AFG 1982 NA NA NA NA
## 4 Afghan… AF AFG 1983 NA NA NA NA
## 5 Afghan… AF AFG 1984 NA NA NA NA
## 6 Afghan… AF AFG 1985 NA NA NA NA
## 7 Afghan… AF AFG 1986 NA NA NA NA
## 8 Afghan… AF AFG 1987 NA NA NA NA
## 9 Afghan… AF AFG 1988 NA NA NA NA
## 10 Afghan… AF AFG 1989 NA NA NA NA
## # … with 7,230 more rows, and 52 more variables: new_sp_m4554 <int>,
## # new_sp_m5564 <int>, new_sp_m65 <int>, new_sp_f014 <int>,
## # new_sp_f1524 <int>, new_sp_f2534 <int>, new_sp_f3544 <int>,
## # new_sp_f4554 <int>, new_sp_f5564 <int>, new_sp_f65 <int>,
## # new_sn_m014 <int>, new_sn_m1524 <int>, new_sn_m2534 <int>,
## # new_sn_m3544 <int>, new_sn_m4554 <int>, new_sn_m5564 <int>,
## # new_sn_m65 <int>, new_sn_f014 <int>, new_sn_f1524 <int>,
## # new_sn_f2534 <int>, new_sn_f3544 <int>, new_sn_f4554 <int>,
## # new_sn_f5564 <int>, new_sn_f65 <int>, new_ep_m014 <int>,
## # new_ep_m1524 <int>, new_ep_m2534 <int>, new_ep_m3544 <int>,
## # new_ep_m4554 <int>, new_ep_m5564 <int>, new_ep_m65 <int>,
## # new_ep_f014 <int>, new_ep_f1524 <int>, new_ep_f2534 <int>,
## # new_ep_f3544 <int>, new_ep_f4554 <int>, new_ep_f5564 <int>,
## # new_ep_f65 <int>, newrel_m014 <int>, newrel_m1524 <int>,
## # newrel_m2534 <int>, newrel_m3544 <int>, newrel_m4554 <int>,
## # newrel_m5564 <int>, newrel_m65 <int>, newrel_f014 <int>,
## # newrel_f1524 <int>, newrel_f2534 <int>, newrel_f3544 <int>,
## # newrel_f4554 <int>, newrel_f5564 <int>, newrel_f65 <int>
شرح لبعض الرموز
1- Country
2- اختصار لاسم الدولة iso2 and iso
3- year
4- من العمودالرابع نلاحظ المتغيرات غير مفهومة وشرحها هو كالتالي
اول ثلاث احرف ترمز الى ان الحاله old او new
الجزء بين “ـ” او الحرف الرابع يرمز الى
rel stands for cases of relapseep stands for cases of extrapulmonary TBsn stands for cases of pulmonary TB that could not be diagnosed by a pulmonary smear (smear negative)sp stands for cases of pulmonary TB that could be diagnosed be a pulmonary smear (smear positive)الحرف السادس يرمز الى الجنس “male” and “female”
الجزء الباقي يرمز الى العمر
014 = 0 – 14 years old1524 = 15 – 24 years old2534 = 25 – 34 years old3544 = 35 – 44 years old4554 = 45 – 54 years old5564 = 55 – 64 years old65 = 65 or olderشاهد case study 12.6
في الكتاب
ال relational data تاتي في غالب الاحوال من قواعد البيانات (RDBMS)
وهي البيانات التكون مقسمة على اكثر من جدول مثلا ان يكون لديك جدول للرواتب وجدول لمعلومات الموظف الشخصية
.هناك ثلاث طرق لاضافة اكثر من جدول الى بعض
1- Mutating joins: اضافه اعمده جديده من جدول A تتطابق احد اعمدتها مع احد اعمدة جدول B
2- Filtering joins : ازاله بعض المشاهدات من الجدول A التي تطابق البيانات في احد الاعمدة من الجدولB
3- Set operations: مثل عمليات المجموعات في الرياضيات (تقاطع، اتحاد)
للتوضيح اكثر سوف نعمل على عده جداول تخص شركات الطيران من مكتبة nycflights13
الجداول هي:
airlinesتعطينا اسم شركه الطيران مع الرمز الثنائي للشركة.
```r
airlines
```
```
## # A tibble: 16 x 2
## carrier name
## <chr> <chr>
## 1 9E Endeavor Air Inc.
## 2 AA American Airlines Inc.
## 3 AS Alaska Airlines Inc.
## 4 B6 JetBlue Airways
## 5 DL Delta Air Lines Inc.
## 6 EV ExpressJet Airlines Inc.
## 7 F9 Frontier Airlines Inc.
## 8 FL AirTran Airways Corporation
## 9 HA Hawaiian Airlines Inc.
## 10 MQ Envoy Air
## 11 OO SkyWest Airlines Inc.
## 12 UA United Air Lines Inc.
## 13 US US Airways Inc.
## 14 VX Virgin America
## 15 WN Southwest Airlines Co.
## 16 YV Mesa Airlines Inc.
```
airportsتعطينا معلومات المطار. نستطيع ان نميز كل مطار عن طريق العمود faa اي رمز المطار .
```r
airports
```
```
## # A tibble: 1,458 x 8
## faa name lat lon alt tz dst tzone
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 04G Lansdowne Airport 41.1 -80.6 1044 -5 A America/New_Yo…
## 2 06A Moton Field Municipal A… 32.5 -85.7 264 -6 A America/Chicago
## 3 06C Schaumburg Regional 42.0 -88.1 801 -6 A America/Chicago
## 4 06N Randall Airport 41.4 -74.4 523 -5 A America/New_Yo…
## 5 09J Jekyll Island Airport 31.1 -81.4 11 -5 A America/New_Yo…
## 6 0A9 Elizabethton Municipal … 36.4 -82.2 1593 -5 A America/New_Yo…
## 7 0G6 Williams County Airport 41.5 -84.5 730 -5 A America/New_Yo…
## 8 0G7 Finger Lakes Regional A… 42.9 -76.8 492 -5 A America/New_Yo…
## 9 0P2 Shoestring Aviation Air… 39.8 -76.6 1000 -5 U America/New_Yo…
## 10 0S9 Jefferson County Intl 48.1 -123. 108 -8 A America/Los_An…
## # … with 1,448 more rows
```
planesيعطينا معلومات كل طائرة، نستطيع تمييز كل طائره باستخدام العمود tailnum .
```r
planes
```
```
## # A tibble: 3,322 x 9
## tailnum year type manufacturer model engines seats speed engine
## <chr> <int> <chr> <chr> <chr> <int> <int> <int> <chr>
## 1 N10156 2004 Fixed wing m… EMBRAER EMB-1… 2 55 NA Turbo-…
## 2 N102UW 1998 Fixed wing m… AIRBUS INDUST… A320-… 2 182 NA Turbo-…
## 3 N103US 1999 Fixed wing m… AIRBUS INDUST… A320-… 2 182 NA Turbo-…
## 4 N104UW 1999 Fixed wing m… AIRBUS INDUST… A320-… 2 182 NA Turbo-…
## 5 N10575 2002 Fixed wing m… EMBRAER EMB-1… 2 55 NA Turbo-…
## 6 N105UW 1999 Fixed wing m… AIRBUS INDUST… A320-… 2 182 NA Turbo-…
## 7 N107US 1999 Fixed wing m… AIRBUS INDUST… A320-… 2 182 NA Turbo-…
## 8 N108UW 1999 Fixed wing m… AIRBUS INDUST… A320-… 2 182 NA Turbo-…
## 9 N109UW 1999 Fixed wing m… AIRBUS INDUST… A320-… 2 182 NA Turbo-…
## 10 N110UW 1999 Fixed wing m… AIRBUS INDUST… A320-… 2 182 NA Turbo-…
## # … with 3,312 more rows
```
weatherيعطينا معلومات الطقس في مطارات NYC لكل ساعة .
```r
weather
```
```
## # A tibble: 26,115 x 15
## origin year month day hour temp dewp humid wind_dir wind_speed
## <chr> <int> <int> <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 EWR 2013 1 1 1 39.0 26.1 59.4 270 10.4
## 2 EWR 2013 1 1 2 39.0 27.0 61.6 250 8.06
## 3 EWR 2013 1 1 3 39.0 28.0 64.4 240 11.5
## 4 EWR 2013 1 1 4 39.9 28.0 62.2 250 12.7
## 5 EWR 2013 1 1 5 39.0 28.0 64.4 260 12.7
## 6 EWR 2013 1 1 6 37.9 28.0 67.2 240 11.5
## 7 EWR 2013 1 1 7 39.0 28.0 64.4 240 15.0
## 8 EWR 2013 1 1 8 39.9 28.0 62.2 250 10.4
## 9 EWR 2013 1 1 9 39.9 28.0 62.2 260 15.0
## 10 EWR 2013 1 1 10 41 28.0 59.6 260 13.8
## # … with 26,105 more rows, and 5 more variables: wind_gust <dbl>, precip <dbl>,
## # pressure <dbl>, visib <dbl>, time_hour <dttm>
```
ال Diagram التالي يشرح العلاقات بين جميع الجداول السابقة
الهدف من ال Diagram هو ان نعرف كيف نربط الجداول السابقة مع بعض .
مثلا
flights يرتبط ب planes عن طريق العمود tailnum .
flights يرتبط ب airlines عن طريق العمود carrier .
flights يرتبط ب airports بطريقتين الاولى عن طريق origin وذالك اذا اردنا الحصول عل معلومات مطار الاقلاع والثانيه عن طريق dest وذالك اذا اردنا الحصول على معلومات مطار الوصول .
flights يرتبط ب weather عن طريق الاعمده التي تثمل الوقت ( year, month, day and hour ) وعمود الموقع origin
primary key : يميز كل مشاهدة في جدولها
مثال : planes$tailnum هي primary key للجدول plans لان كل طائره لها مشاهدة واحدة فقط
foreign key : يميز كل مشاهدة في جدول آخر
مثال : flights$tailnum هو foreign key لانه موجود في جدول flights لاكن يمكن ان يتكرر (لان الطائره ممكن تعمل اكثر من رحلة) لاكنة primaray key لاكن من خلالة يمكننا الربط مع جدول planes
الان لنتاكد ان ان الشروط تنطبق هنا
#tailnum هنا نختبر اذا كان لدينا اكثر من قيمه في العمود
# planes في الجدول primary key حيث انه هو ال
planes %>%
count(tailnum) %>%
filter(n > 1)## # A tibble: 0 x 2
## # … with 2 variables: tailnum <chr>, n <int>
## # A tibble: 3 x 6
## year month day hour origin n
## <int> <int> <int> <int> <chr> <int>
## 1 2013 11 3 1 EWR 2
## 2 2013 11 3 1 JFK 2
## 3 2013 11 3 1 LGA 2
لاحظ ان الاعمده التي يجب ان تكون primary key في الجدول weather، لم تفي بالشروط لذالك لايمكن اعتبارها primary key .
احيانا قد يكون كل صف بالفعل يمثل مشاهدة لاكن لانستطيع ايجاد التوليفة المناسبه من الاعمدة لتشكيل primary key
في هذه الحاله ربما تحتاج الى البحث عن عمود يساعد في جعل كل مشاهده مميزه او يمكنك اضافه row_number() ويسمى هذا ال key surrogate key
مصممو قواعد البيانات لايحبون تكرار البيانات لذالك يقومون بعملية تسمى .Decomposition
.الهدف من هذه العملية هي ازاله البيانات المتكرره ووضعها في جدول اخر
مثال
كل شركة تزود موضفيها برقم وضيفي، الهدف منه هو تمييز كل موضف عن الاخر، بحيث اننا نستطيع معرفه .معلومات الموضف بمعرفه رقمة الوضيفي
الان لنفرض اننا نريد عمل قاعده بيانات توضح رواتب الموظفين. هل نضع جميع معلومات الموضفين في جدول .الرواتب او نكتفي فقط برقمهم الوظيفي
مصممين قواعد البيانات يرون انه من الافضل عمل جدولين. واحد لمعلومات الموضفين والاخر للرواتب
.بحيث يكون الرقم الوضيفي هو الرابط بين الجدولين
الان نطلق على الرقم الوضيفي لقب Primary key
لنفرض مثلا بان لدينا جدول اخر يمثل الحضور والغياب. لجميع الموضفين خلال السنه الفائته، يحتوي مثلا على رقم الموضف و التاريخ و الحاله
في هذه الحاله الرقم الوضيفي يفقد في هذا الجدول لقب Primary key
لاننا لانستطيع من خلالة تميز المشاهدات. ويكون ال Primary key هو تاريخ اليوم + الرقم الوضيفي. ويصبح الرقم الوضيفي foreign key
لاحظ انه الان الرقم الوضيفي اصبح Primary key في جدول العلومات الشخصية و جدول الرواتب
و foreign key
في جدول الحضور
واصبح الرقم الوضيفي مع التاريخ هو Primary key في جدول الحضور
وهي عمليه عمليه شبية ب mutate() .حيث نقوم بإضافة اعمده اضافيه
على سبيل المثال flights2 خذ الجدول
## # A tibble: 336,776 x 8
## year month day hour origin dest tailnum carrier
## <int> <int> <int> <dbl> <chr> <chr> <chr> <chr>
## 1 2013 1 1 5 EWR IAH N14228 UA
## 2 2013 1 1 5 LGA IAH N24211 UA
## 3 2013 1 1 5 JFK MIA N619AA AA
## 4 2013 1 1 5 JFK BQN N804JB B6
## 5 2013 1 1 6 LGA ATL N668DN DL
## 6 2013 1 1 5 EWR ORD N39463 UA
## 7 2013 1 1 6 EWR FLL N516JB B6
## 8 2013 1 1 6 LGA IAD N829AS EV
## 9 2013 1 1 6 JFK MCO N593JB B6
## 10 2013 1 1 6 LGA ORD N3ALAA AA
## # … with 336,766 more rows
الان سوف نقوم باضافة اسماء شركات الطيران من جدول airlines
## # A tibble: 336,776 x 7
## year month day hour tailnum carrier name
## <int> <int> <int> <dbl> <chr> <chr> <chr>
## 1 2013 1 1 5 N14228 UA United Air Lines Inc.
## 2 2013 1 1 5 N24211 UA United Air Lines Inc.
## 3 2013 1 1 5 N619AA AA American Airlines Inc.
## 4 2013 1 1 5 N804JB B6 JetBlue Airways
## 5 2013 1 1 6 N668DN DL Delta Air Lines Inc.
## 6 2013 1 1 5 N39463 UA United Air Lines Inc.
## 7 2013 1 1 6 N516JB B6 JetBlue Airways
## 8 2013 1 1 6 N829AS EV ExpressJet Airlines Inc.
## 9 2013 1 1 6 N593JB B6 JetBlue Airways
## 10 2013 1 1 6 N3ALAA AA American Airlines Inc.
## # … with 336,766 more rows
*()_joinx <- tribble(
~key, ~val_x,
1, "x1",
2, "x2",
3, "x3"
)
y <- tribble(
~key, ~val_y,
1, "y1",
2, "y2",
4, "y3"
)في الربط الداخلي نبقي فقط المشاهدات المتشابهة الموجوده في العمود key
في كل من الجدول x and y
## # A tibble: 2 x 3
## key val_x val_y
## <dbl> <chr> <chr>
## 1 1 x1 y1
## 2 2 x2 y2
في الربط الخارجي نبقي كل المشاهدات الموجودة في الجدول x
مع مايتقاطع معها من الجدول
y
## # A tibble: 3 x 3
## key val_x val_y
## <dbl> <chr> <chr>
## 1 1 x1 y1
## 2 2 x2 y2
## 3 3 x3 <NA>
## # A tibble: 3 x 3
## key val_x val_y
## <dbl> <chr> <chr>
## 1 1 x1 y1
## 2 2 x2 y2
## 3 4 <NA> y3
يبقي فقط المشاهدات الموجودة في x التي تتطابق مع
y لاكن بدون تضمين قيم y
## # A tibble: 2 x 2
## key val_x
## <dbl> <chr>
## 1 1 x1
## 2 2 x2