R: قوائم R: «الحاوية الشاملة» في لغة R

آخر تحديث: 2026-08-26

في الدروس الثلاثة السابقة، تعرفنا على المتجهات والمصفوفات — وكلاهما يتطلب أن تكون جميع العناصر من نفس النوع. لكن في الواقع، غالبًا ما تكون البيانات غير متجانسة: فمعلومات الطلاب تشمل رقم الهوية (أرقام)، والاسم (أحرف)، والدرجات (متجهات). ويستخدم لغة R القوائم لحل مشكلة تخزين البيانات غير المتجانسة.

تُعد القوائم بمثابة «سكين الجيش السويسري» الحقيقي في لغة R — فهي يمكن أن تحتوي على أي نوع (متجهات، مصفوفات، إطارات بيانات، دوال، أو حتى قوائم أخرى). وإطارات البيانات data.frame هي في جوهرها «قوائم من المتجهات ذات الطول المتساوي». ويُعد هذا الدرس أساسيًا للمرحلة الأولى.

1. ما ستتعلمه



2. قصة حول إدارة معلومات الطلاب

(1) المشكلة: تخزين البيانات غير المتجانسة

يعمل إيفان في قسم الموارد البشرية ويحتاج إلى تخزين المعلومات الكاملة الخاصة بموظف واحد:

TEXT 📖 للعرض فقط
Employee ID:2024001(Numbers)
Name:Alice(Character)
Department:Data Analysis Department(Character)
Salary:15000(Numbers)
Skills:c("R", "Python", "SQL")(Character vector)

إذا استخدمت متجهًا، فيجب أن تكون جميع العناصر من نفس النوع — لن يعمل ذلك. وإذا استخدمت إطار بيانات، فيجب أن يحتوي كل عمود على نفس عدد الأعمدة — لن يعمل ذلك أيضًا.

(2) الحل باستخدام القوائم

R
# 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) قواعد النحو الأساسية

R
list(key1 = value1, key2 = value2, ...)

(2) 5 طرق للإبداع

R
# 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) مقارنة القوائم والمتجهات

100%
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) [[ ]] مقابل [ ]: الاختلافات الرئيسية

R
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"]]

R
# $ 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) الوصول إلى القوائم المتداخلة

R
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) تعديل عنصر

R
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) إضافة عنصر

R
# 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) حذف العناصر

R
# Assign NULL to delete
person$phone <- NULL

# Or
person[["address"]] <- NULL

(4) عرض بنية القائمة

R
# 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 تطبيق دالة على كل عنصر:

R
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 + وظيفة مخصصة

R
# 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()

R
# lapply Back to List,sapply Simplify to a vector
sapply(mixed_list, length)
# numbers    chars  logical 
#       5        3        3


7. دمج القوائم: c() و unlist()

(1) c() دمج القوائم

R
# 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() إلى متجه

R
# 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(): تحويل قائمة إلى بيئة

حوّل كل عنصر في القائمة إلى متغير عام:

R
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

R
# 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. مثال كامل: إدارة سجلات الموظفين

فيما يلي مثال على مسار عمل كامل يربط بين جميع ميزات القوائم التي تم تناولها في هذا الدرس.

▶ مثال: إدارة ملفات الموظفين

R 📖 للعرض فقط
# ============================================
# 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
56 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

النتائج المتوقعة (مقتطف):

TEXT 📖 للعرض فقط
=== 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")).


📖 ملخص


📝 تمارين

  1. تمرين أساسي: أنشئ قائمة باسم student تحتوي على أربعة حقول: name (حرف)، age (رقم)، scores (متجه قيم c(85, 90, 92))، وpassed (قيمة منطقية). اطبع بنية القائمة (str())، وقم بالوصول إلى name وscores[2].

  2. المشكلة الأساسية: أنشئ قائمة متداخلة family تحتوي على ثلاث قوائم فرعية father وmother وchild (تحتوي كل منها على name وage). قم بالوصول إلى child$age واطبعها.

  3. المشكلة الأساسية: استخدم lapply() لحساب «الراتب السنوي» (salary * 12) دفعة واحدة لقائمة مكونة من ثلاثة موظفين (تقوم بإنشائها بنفسك)، واستخدم sapply() لتبسيط النتائج وتحويلها إلى ناتج متجه.

  4. مشكلة متقدمة: اكتب دالة create_employee(name, dept, salary) تُرجع قائمة مسماة (تحتوي على id، name، dept، salary، skills=متجه فارغ، is_active=TRUE). استدعِها ثلاث مرات لإنشاء ثلاثة موظفين وتخزينهم في قائمة كبيرة. استخدم sapply() لاستخراج «الراتب السنوي» لجميع الموظفين.

  5. التحدي: اكتب برنامجًا نصيًّا لمحاكاة إدارة «مكتبة»: ① أنشئ قائمة library تحتوي على 3 كتب (يحتوي كل كتاب على 4 حقول: title وauthor وyear وavailable)؛ ② استخدم lapply لتعيين قيمة available لجميع الكتب على FALSE (مستعارة)؛ ③ استخدم sapply للعثور على جميع الكتب المتاحة للاستعارة (available == TRUE)؛ ④ أضف كتابًا جديدًا إلى القائمة.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%