R: قوائم R: «الحاوية الشاملة» في لغة R
آخر تحديث: 2026-08-26
في الدروس الثلاثة السابقة، تعرفنا على المتجهات والمصفوفات — وكلاهما يتطلب أن تكون جميع العناصر من نفس النوع. لكن في الواقع، غالبًا ما تكون البيانات غير متجانسة: فمعلومات الطلاب تشمل رقم الهوية (أرقام)، والاسم (أحرف)، والدرجات (متجهات). ويستخدم لغة R القوائم لحل مشكلة تخزين البيانات غير المتجانسة.
تُعد القوائم بمثابة «سكين الجيش السويسري» الحقيقي في لغة R — فهي يمكن أن تحتوي على أي نوع (متجهات، مصفوفات، إطارات بيانات، دوال، أو حتى قوائم أخرى). وإطارات البيانات data.frame هي في جوهرها «قوائم من المتجهات ذات الطول المتساوي». ويُعد هذا الدرس أساسيًا للمرحلة الأولى.
1. ما ستتعلمه
- تُنشئ الدالة list() حاوية من أي نوع
- قائمة الأسماء (البحث حسب الاسم)
- 4 أوضاع وصول (
[[ ]][ ]$[[ "name" ]]) - تعديل العناصر في قائمة وإضافتها وحذفها
- lapply() لمعالجة القوائم دفعة واحدة
- القوائم المتداخلة
- تقوم الدالة list2env() بتحويل قائمة إلى متغيرات بيئة
2. قصة حول إدارة معلومات الطلاب
(1) المشكلة: تخزين البيانات غير المتجانسة
يعمل إيفان في قسم الموارد البشرية ويحتاج إلى تخزين المعلومات الكاملة الخاصة بموظف واحد:
Employee ID:2024001(Numbers)
Name:Alice(Character)
Department:Data Analysis Department(Character)
Salary:15000(Numbers)
Skills:c("R", "Python", "SQL")(Character vector)
إذا استخدمت متجهًا، فيجب أن تكون جميع العناصر من نفس النوع — لن يعمل ذلك. وإذا استخدمت إطار بيانات، فيجب أن يحتوي كل عمود على نفس عدد الأعمدة — لن يعمل ذلك أيضًا.
(2) الحل باستخدام القوائم
# One list Fits all types
employee <- list(
id = 2024001,
name = "Alice",
department = "Data Analysis Department",
salary = 15000,
skills = c("R", "Python", "SQL"),
is_active = TRUE
)
# Access any field
employee$name # [1] "Alice"
employee$skills[1] # [1] "R"
employee$salary * 12 # [1] 180000 (Annual Salary)
قائمة واحدة تحتوي على 6 حقول من أنواع مختلفة. هذه هي قوة قوائم لغة R.
3. list(): إنشاء قائمة
(1) قواعد النحو الأساسية
list(key1 = value1, key2 = value2, ...)
(2) 5 طرق للإبداع
# 1. List of Names(Most Commonly Used)
person <- list(name = "Alice", age = 25, scores = c(85, 90, 92))
person
# $name
# [1] "Alice"
#
# $age
# [1] 25
#
# $scores
# [1] 85 90 92
# 2. Anonymous List(Browse by Location)
unnamed <- list("a", 1, TRUE)
# [[1]] "a"
# [[2]] 1
# [[3]] TRUE
# 3. Nested Lists
company <- list(
hr = list(name = "HR", count = 5),
it = list(name = "IT", count = 20)
)
# 4. Empty list
empty <- list()
length(empty) # [1] 0
# 5. Use list() to convert vector (Maintain Heterogeneity)
as.list(c(1, 2, 3))
# [[1]] 1
# [[2]] 2
# [[3]] 3
(3) مقارنة القوائم والمتجهات
graph TB
A[R Data Structures] --> B[Vector<br/>Homogeneous<br/>One-dimensional]
A --> C[Matrix<br/>Homogeneous<br/>Two-dimensional]
A --> D[List<br/>Heterogeneous<br/>Any type]
A --> E[Data Frame<br/>List of vectors of equal length<br/>Two-dimensional]
style A fill:#fff3cd
style B fill:#cce5ff
style C fill:#d4edda
style D fill:#f8d7da
style E fill:#e1d4ff
| العقارات | فيكتور c(1, 2, 3) |
القائمة list(1, "a", TRUE) |
|---|---|---|
| نوع العنصر | يجب أن يكون متطابقًا | يمكن أن يكون مختلفًا |
| نوع العنصر | 6 أنواع أساسية | أي كائن في R (بما في ذلك الدوال وإطارات البيانات) |
| زيارة | v[1] |
l[[1]] |
| طول متغير | ❌ | ✅ (يمكن إضافته أو حذفه حسب الرغبة) |
| مشابه لـ Python | tuple |
dict / list |
4. 4 طرق الوصول (النقاط الرئيسية)
(1) 4 أنواع من جداول البحث
| الطريقة | الصيغة | القيمة المرجعة | الغرض |
|---|---|---|---|
[[i]] |
العنصر رقم i | العنصر نفسه | استرداد عنصر واحد |
[i] |
العنصر رقم i | قائمة فرعية | اختيار مجموعة فرعية |
$name |
حسب الاسم | العنصر نفسه | الحصول على العنصر المسمى |
[["name"]] |
حسب الاسم | العنصر نفسه | المكافئ $name |
(2) [[ ]] مقابل [ ]: الاختلافات الرئيسية
person <- list(name = "Alice", age = 25, scores = c(85, 90, 92))
# [[ ]] Return the element itself
person[[1]]
# [1] "Alice" ← Character
# [ ] Return to Sublist
person[1]
# $name
# [1] "Alice" ← Or a list
# Key Differences: Using [ ] after extraction, attempting to access sub-elements may cause an error
person[1]$name # ✅ $name Still accessible(Syntax sugar)
person[1][1] # ❌ This is the 1 element,But there is only one child list $name
# Practical Application:Calculate the average score
mean(person[[3]]) # [1] 89 ← [[3]] Extract the vector
mean(person$scores) # [1] 89 ← $scores Extract the vector
mean(person[3]) # Error: need numeric data
[[ ]] أو $ للوصول إلى العناصر، و[ ] للوصول إلى المجموعات الفرعية. هذا هو الجانب الأكثر إرباكًا في الوصول إلى القوائم في لغة R.
(3) $ مقابل [["name"]]
# $ is syntactic sugar for [[ "name" ]]
person$name # Equivalent person[["name"]]
person[["name"]] # Equivalent person$name
# $ Supports partial matches(Not recommended)
person$na # Automatically matched to "name"
# [1] "Alice"
# [[ ]] Partial matches are not supported(Safer)
person[["na"]] # Error
[[ "name" ]] في كود الإنتاج — فهو واضح ولا يثير أي لبس، ولا يعتمد على التطابقات الجزئية.
(4) الوصول إلى القوائم المتداخلة
company <- list(
hr = list(name = "HR", count = 5, head = "Alice"),
it = list(name = "IT", count = 20, head = "Bob")
)
# Accessing Nested Elements
company$hr$head # [1] "Alice"
company[["it"]][["head"]] # [1] "Bob"
company$it$count # [1] 20
5. تعديل العناصر في قائمة وإضافتها وحذفها
(1) تعديل عنصر
person <- list(name = "Alice", age = 25)
# Edit ([[ ]] or $ both can be changed)
person$age <- 26
person[["age"]]
# [1] 26
# Batch Edit
person$age <- person$age + 1
person$age # [1] 27
(2) إضافة عنصر
# Methods 1: Use $ to add
person$email <- "alice@example.com"
# Methods 2: Use [[ ]] to add
person[["phone"]] <- "13800000000"
# Methods 3: Use c() to merge
person <- c(person, address = "Haidian District, Beijing")
(3) حذف العناصر
# Assign NULL to delete
person$phone <- NULL
# Or
person[["address"]] <- NULL
(4) عرض بنية القائمة
# View Structure(Important Debugging Functions)
str(person)
# List of 4
# $ name : chr "Alice"
# $ age : num 27
# $ email : chr "alice@example.com"
# $ address : chr "Haidian District, Beijing"
# View All Names
names(person)
# [1] "name" "age" "email" "address"
6. lapply(): المعالجة المجمعة للقوائم
(1) لماذا نستخدم lapply؟
قد تختلف أنواع العناصر في القائمة — فلا يمكنك ببساطة استخدام sum() mean(). تتيح لنا الدالة lapply تطبيق دالة على كل عنصر:
mixed_list <- list(
numbers = 1:5,
chars = c("a", "b", "c"),
logical = c(TRUE, FALSE, TRUE)
)
# Calculate the length of each element
lapply(mixed_list, length)
# $numbers
# [1] 5
#
# $chars
# [1] 3
#
# $logical
# [1] 3
(2) lapply + وظيفة مخصصة
# CalculateFor each element,"Abstract"
summary_list <- lapply(mixed_list, function(x) {
if (is.numeric(x)) {
return(list(mean = mean(x), sum = sum(x)))
} else {
return(list(length = length(x), class = class(x)))
}
})
summary_list$numbers
# $mean
# [1] 3
#
# $sum
# [1] 15
(3) تبسيط النتائج باستخدام sapply()
# lapply Back to List,sapply Simplify to a vector
sapply(mixed_list, length)
# numbers chars logical
# 5 3 3
7. دمج القوائم: c() و unlist()
(1) c() دمج القوائم
# Merge Multiple Lists
list1 <- list(a = 1, b = 2)
list2 <- list(c = 3, d = 4)
merged <- c(list1, list2)
merged
# $a 1
# $b 2
# $c 3
# $d 4
(2) تُحول الدالة unlist() إلى متجه
# Flatten the list into a vector(Only for"Atom"Element Valid)
numbers_list <- list(a = 1, b = 2, c = 3)
unlist(numbers_list)
# a b c
# 1 2 3
# Note:The type will be coerced after flattening.
mixed <- list(1, "a", TRUE)
unlist(mixed)
# [1] "1" "a" "TRUE" ← Convert All to Strings
8. list2env(): تحويل قائمة إلى بيئة
حوّل كل عنصر في القائمة إلى متغير عام:
config <- list(
host = "localhost",
port = 3306,
user = "root",
password = "secret"
)
# Convert list elements to environment variables
list2env(config, envir = .GlobalEnv)
# You can use it right away
host # [1] "localhost"
port # [1] 3306
list2env() في تلويث البيئة العامة، لذا يجب استخدامه بحذر. ويُستخدم عادةً في الحالات التي يتم فيها تحميل الإعدادات من ملفات YAML أو JSON.
9. مقارنة بين العمليات الشائعة
(1) c() مقابل list() مقابل unlist()
| الوظيفة | الغرض | المدخلات → المخرجات |
|---|---|---|
c() |
دمج العناصر الذرية | c(1, 2, 3) → المتجهات |
list() |
إنشاء حاوية غير متجانسة | list(1, "a") → قائمة |
unlist() |
تحويل القائمة إلى متجه | unlist(list(1,2,3)) → متجه |
(2) تحويل قائمة إلى DataFrame
# A list of vectors of equal length can be converted data.frame
df <- as.data.frame(list(
name = c("A", "B", "C"),
age = c(20, 25, 30),
score = c(85, 90, 78)
))
print(df)
# name age score
# 1 A 20 85
# 2 B 25 90
# 3 C 30 78
10. مثال كامل: إدارة سجلات الموظفين
فيما يلي مثال على مسار عمل كامل يربط بين جميع ميزات القوائم التي تم تناولها في هذا الدرس.
▶ مثال: إدارة ملفات الموظفين
# ============================================
# Employee File Management
# Features:Manage with Lists 3 Complete information about each employee
# ============================================
# 1. Create 3 List of employees
employees <- list(
alice = list(
id = 2024001,
name = "Alice",
department = "Data Analysis Department",
salary = 15000,
skills = c("R", "Python", "SQL"),
projects = c("User Profiles", "Sales Forecast")
),
bob = list(
id = 2024002,
name = "Bob",
department = "Engineering Department",
salary = 18000,
skills = c("Java", "Go", "Docker"),
projects = c("API Refactoring", "Microservice Migration")
),
charlie = list(
id = 2024003,
name = "Charlie",
department = "Product Department",
salary = 20000,
skills = c("Figma", "Axure"),
projects = c("V2.0 Design", "User Research")
)
)
# 2. View a specific employee's full profile
cat("=== Alice Full Information ===\n")
str(employees$alice)
# List of 6
# $ id : num 2024001
# $ name : chr "Alice"
# $ department : chr "Data Analysis Department"
# $ salary : num 15000
# $ skills : chr [1:3] "R" "Python" "SQL"
# $ projects : chr [1:2] "User Profiles" "Sales Forecast"
# 3. Use sapply for batch processing
cat("\n=== Annual Salaries of All Employees ===\n")
annual_salaries <- sapply(employees, function(emp) emp$salary * 12)
print(annual_salaries)
# alice bob charlie
# 180000 216000 240000
# 4. Use sapply to count skills
cat("\n=== Number of skills per employee ===\n")
skill_counts <- sapply(employees, function(emp) length(emp$skills))
print(skill_counts)
# alice bob charlie
# 3 3 2
# 5. Use split for grouping by department
cat("\n=== Grouped by Department ===\n")
dept_groups <- split(
sapply(employees, function(emp) emp$name),
sapply(employees, function(emp) emp$department)
)
print(dept_groups)
# $Data Analysis Department
# [1] "Alice"
#
# $Engineering Department
# [1] "Bob"
#
# $Product Department
# [1] "Charlie"
# 6. Add a New Employee
employees$diana <- list(
id = 2024004,
name = "Diana",
department = "Data Analysis Department",
salary = 16000,
skills = c("R", "Tableau"),
projects = c("Data Visualization")
)
cat("\n=== Add Diana after ===\n")
cat("Total Number of Employees:", length(employees), "\n")
cat("Diana Department:", employees$diana$department, "\n")
# 7. Identify the employee with the highest salary
top_salary <- which.max(sapply(employees, function(emp) emp$salary))
cat("\nHighest Salary:", names(employees)[top_salary], "(", max(sapply(employees, function(emp) emp$salary)), "USD)\n")
# 8. Retrieve the skills of all employees(Merge all skill vectors)
all_skills <- unlist(lapply(employees, function(emp) emp$skills))
cat("\n=== Company-wide Skills Summary ===\n")
print(table(all_skills))
# all_skills
# Axure Docker Figma Go Java Python R SQL Tableau
# 1 1 1 1 1 1 2 1 1
النتائج المتوقعة (مقتطف):
=== Alice Full Information ===
List of 6
$ id : num 2024001
$ name : chr "Alice"
...
=== Annual Salaries of All Employees ===
alice bob charlie
180000 216000 240000
=== Company-wide Skills Summary ===
all_skills
Axure Docker Figma Go Java Python R SQL Tableau
1 1 1 1 1 1 2 1 1
❓ أسئلة شائعة
س: ما الفرق بين
[[ ]]و[ ]؟ ج: تُرجع[[ ]]العنصر نفسه (الذي يمكن معالجته لاحقًا)، بينما تُرجع[ ]قائمة فرعية (لا يمكن معالجة عناصرها لاحقًا). استخدم[[ ]]أو$لاسترداد العناصر، و[ ]لاسترداد المجموعات الفرعية. هذا هو أحد أكثر الجوانب إرباكًا في صيغة لغة R.
س: هل
$و[[ "name" ]]متطابقان؟ ج: إنهما متكافئان بشكل أساسي. ومع ذلك، فإن$يدعم المطابقة الجزئية ($naيطابق "name" تلقائيًا)، بينما[[ ]]لا يدعم ذلك. ومن الأفضل استخدام[[ "name" ]]في كود الإنتاج.
س: ما هي العلاقة بين القوائم والمتجهات؟ ج: المتجهات متجانسة وأحادية البعد (جميع العناصر من نفس النوع)، في حين أن القوائم غير متجانسة ويمكن أن تكون بأي بُعد (يمكن أن تكون العناصر من أي نوع).
data.frameفي جوهرها، القائمة هي «قائمة من المتجهات ذات الطول المتساوي» — وفهم القوائم يعني فهم إطارات البيانات.
س: كيف أختار بين
lapply()وsapply()؟ ج: تُرجعlapply()قائمة (مع الحفاظ على البنية)، بينما تُبسطsapply()النتيجة إلى متجه أو مصفوفة (مما يجعلها أسهل في القراءة). استخدمsapplyللمهام اليومية، وlapplyلسلسلة عمليات البرمجة الوظيفية.
س:
unlist()متى ينبغي استخدامها؟ ج: لـ«تسوية» قائمة إلى متجه — ولكن لا يمكن تسوية سوى العناصر الأحادية. أما الأنواع المختلطة فسيتم تحويلها قسريًّا (على سبيل المثال، list(1, "a") → c("1", "a")).
📖 ملخص
- القوائم هي «حاويات عامة» في لغة R: فهي يمكن أن تحتوي على أي نوع (متجهات، مصفوفات، دوال، أو قوائم أخرى)
- 4 طرق للوصول:
[[i]]$name[[ "name" ]](استرجاع العناصر) مقابل[i](استرجاع مجموعة فرعية) $nameيدعم المطابقة الجزئية (غير موصى به)،[[ "name" ]]المطابقة التامة (موصى به) إضافة/حذف/تعديل:l$new <- x(إضافة)/l$old <- NULL(حذف)/l$old <- new_x(تعديل)lapply()يؤدي تطبيق دالة على كل عنصر إلى إرجاع قائمة؛sapply()تم تبسيطها إلى متجهunlist()تحويل قائمة إلى متجه (سيتم تحويل العناصر غير المتجانسة)- يُعد إطار البيانات
data.frameفي جوهره «قائمة من المتجهات ذات الطول المتساوي» — وفهم القوائم هو شرط أساسي لفهم أطر البيانات.
📝 تمارين
-
تمرين أساسي: أنشئ قائمة باسم
studentتحتوي على أربعة حقول:name(حرف)،age(رقم)،scores(متجه قيم c(85, 90, 92))، وpassed(قيمة منطقية). اطبع بنية القائمة (str())، وقم بالوصول إلىnameوscores[2]. -
المشكلة الأساسية: أنشئ قائمة متداخلة
familyتحتوي على ثلاث قوائم فرعيةfatherوmotherوchild(تحتوي كل منها علىnameوage). قم بالوصول إلىchild$ageواطبعها. -
المشكلة الأساسية: استخدم
lapply()لحساب «الراتب السنوي» (salary * 12) دفعة واحدة لقائمة مكونة من ثلاثة موظفين (تقوم بإنشائها بنفسك)، واستخدمsapply()لتبسيط النتائج وتحويلها إلى ناتج متجه. -
مشكلة متقدمة: اكتب دالة
create_employee(name, dept, salary)تُرجع قائمة مسماة (تحتوي على id، name، dept، salary، skills=متجه فارغ، is_active=TRUE). استدعِها ثلاث مرات لإنشاء ثلاثة موظفين وتخزينهم في قائمة كبيرة. استخدمsapply()لاستخراج «الراتب السنوي» لجميع الموظفين. -
التحدي: اكتب برنامجًا نصيًّا لمحاكاة إدارة «مكتبة»: ① أنشئ قائمة
libraryتحتوي على 3 كتب (يحتوي كل كتاب على 4 حقول:titleوauthorوyearوavailable)؛ ② استخدمlapplyلتعيين قيمةavailableلجميع الكتب على FALSE (مستعارة)؛ ③ استخدمsapplyللعثور على جميع الكتب المتاحة للاستعارة (available == TRUE)؛ ④ أضف كتابًا جديدًا إلى القائمة.