زمینه (Background):
یکی از پرسشهای بنیادین در علوم شناختی و فلسفه ذهن این است که آیا تصمیمگیری انسان در شرایط کاملاً یکسان، تکرارپذیر (deterministic) است یا تصادفی (stochastic). این پژوهش به بررسی تجربی این مسئله از طریق یک آزمون رفتاری کنترلشده میپردازد.
هدف (Objective):
سنجش میزان همسانی (consistency) انتخابهای افراد هنگام مواجهه با تحریکهای بصری یکسان در دو زمان مختلف، در شرایطی که:
روش (Method):
75 شرکتکننده (18-45 سال) در یک آزمون تصمیمگیری سریع (Rapid Decision Task) شرکت میکنند. در هر مرحله، 10 دایره طوسی یکسان در موقعیتهای تصادفی نمایش داده میشود و شرکتکننده باید در عرض 1.8 ثانیه یکی را انتخاب کند. از میان 70 مرحله اصلی، 15 مرحله بهصورت پنهان تکرار میشوند تا میزان همسانی انتخابها در تکرارها سنجیده شود.
شاخصهای اندازهگیری:
پیشبینی نتایج:
در صورتی که تصمیمگیری کاملاً تصادفی باشد، (احتمال شانس). هرگونه انحراف معنادار از این مقدار نشاندهنده وجود الگوی deterministic یا bias شناختی است.
کلمات کلیدی:
تصمیمگیری، آزاد اراده، قطعیت در مقابل تصادفیبودن، حافظه ضمنی، سوگیریهای شناختی
یکی از قدیمیترین پرسشهای فلسفه و علوم شناختی این است:
آیا انسان در شرایط کاملاً یکسان، همان تصمیم را میگیرد؟
این سوال در سه حوزه اهمیت دارد:
فلسفه ذهن (Philosophy of Mind):
آیا اراده آزاد (free will) وجود دارد یا تصمیمات ما محصول حتمی فرآیندهای مغزی هستند؟
علوم اعصاب (Neuroscience):
آیا فعالیت نورونها deterministic است یا noise تصادفی (neural noise) نقش دارد؟
روانشناسی شناختی (Cognitive Psychology):
آیا تصمیمگیری تحت تأثیر الگوهای ناخودآگاه و یادگیری ضمنی است؟
الف) آزمایشهای لیبت (Libet, 1983)
بنجامین لیبت نشان داد که فعالیت مغزی مرتبط با تصمیمگیری 300-500 میلیثانیه قبل از آگاهی آگاهانه رخ میدهد.
Timeline:
t = -500ms: Readiness Potential (RP) در مغز شروع میشود
↓
t = -200ms: فرد آگاهانه قصد حرکت میکند
↓
t = 0ms: حرکت انجام میشود
نتیجه: تصمیمات ممکن است قبل از آگاهی، در سطح ناخودآگاه شکل بگیرند.
محدودیت: این آزمایش درباره "زمان آگاهی" است نه "قابلیت تکرار تصمیم".
ب) نظریه دو سیستمی کانمن (Kahneman, 2011)
دانیل کانمن (برنده نوبل) دو سیستم تصمیمگیری را تعریف کرد:
| سیستم 1 (System 1) | سیستم 2 (System 2) |
|---|---|
| سریع و خودکار | آهسته و تحلیلی |
| ناخودآگاه | آگاهانه |
| کمهزینه شناختی | پرهزینه شناختی |
| مستعد سوگیری | منطقیتر |
در آزمایش ما:
با محدود کردن زمان به 1.8 ثانیه، سیستم 1 را فعال میکنیم تا تصمیمات شهودی (intuitive) را بسنجیم.
ج) حافظه ضمنی (Implicit Memory) - اسکوایر (Squire, 1992)
تحقیقات روی بیماران آمنزی نشان داده که دو نوع حافظه وجود دارد:
مثال کلاسیک:
بیمار H.M. (لوب گیجگاهی آسیب دیده) نمیتوانست رویدادهای جدید را به خاطر بسپارد، اما مهارتهای حرکتی جدید یاد میگرفت بدون اینکه بداند یاد گرفته!
کاربرد در آزمایش ما:
با ایجاد فاصله زمانی بین تکرارها، حافظه صریح را حذف میکنیم اما ممکن است حافظه ضمنی باقی بماند.
مطالعات قبلی به این سوال پاسخ ندادهاند:
آیا در شرایطی که:
- محرک کاملاً یکسان باشد
- حافظه آگاهانه وجود نداشته باشد
- زمان تصمیمگیری محدود باشد (پیش از تحلیل آگاهانه)
انسان همان گزینه را انتخاب میکند؟
این آزمایش برای اولین بار این شرایط را بهصورت همزمان کنترل میکند.
در غیاب حافظه آگاهانه و با محدودیت زمانی، آیا انسان در مواجهه با تحریکهای یکسان، تصمیمات یکسانی میگیرد؟
به زبان ساده:
توضیح احتمال 10%:
چون 10 گزینه داریم، احتمال اینکه تصادفاً همان گزینه انتخاب شود:
به زبان ساده:
اگر حافظه ضمنی وجود داشته باشد، زمان واکنش در مراحل تکراری کوتاهتر خواهد بود (حتی اگر فرد متوجه تکرار نشود).
استدلال علمی:
Priming Effect (اثر آمادهسازی) - رویارویی قبلی با یک محرک، پردازش بعدی آن را تسریع میکند.
به زبان ساده:
انسانها دارای سوگیریهای مکانی هستند (مثلاً ترجیح مرکز صفحه یا راست/چپ).
پیشبینی:
مبنای نظری:
Center Bias در eye-tracking و UI/UX بهخوبی مستند شده است.
به زبان ساده:
در ابتدای آزمایش، انتخابها پراکندهتر هستند. با گذشت زمان، فرد الگوی خاصی پیدا میکند (Entropy کاهش مییابد).
مبنای نظری:
Procedural Learning (یادگیری رویهای) - حتی در تکالیف ساده، مغز الگو میسازد.
📊 راهنمای رنگبندی:
سوگیری مکانی
تمایل به انتخاب مرکز یا گوشهها
حافظه ضمنی
تأثیر ناخودآگاه رویارویی قبلی
نویز تصادفی
عدم قطعیت ذاتی تصمیم
هدف آزمایش:
جداسازی و اندازهگیری نقش هر یک از عوامل فوق در فرآیند تصمیمگیری
P(\text{choice}_i) = f(
\underbrace{\text{Bias}_i}_{\text{سوگیری مکانی}},
\underbrace{M_i}_{\text{حافظه ضمنی}},
\underbrace{\epsilon}_{\text{نویز تصادفی}}
)
نوع مطالعه:
چرا این طرح؟
بزرگسالان سالم 18-45 سال با بینایی طبیعی یا اصلاحشده
محاسبه با استفاده از G*Power:
Test: Binomial test (one-tailed)
Effect size: h = 0.35 (medium)
α = 0.05
Power (1-β) = 0.80
→ n = 67 participants
با احتساب ریزش 10%:
مزایای PWA:
آزمایش شامل 80 مرحله (Trial) است:
└─ آشنایی با مکانیک بازی
Unique: 50 صحنه منحصر به فرد
Implicit Repeats: 15 صحنه
Explicit Repeats: 5 صحنه
Catch Trials: 5 صحنه
⏱️ مدت زمان تقریبی: 4-5 دقیقه
هدف:
آشنایی شرکتکننده با رابط کاربری و مکانیک تکلیف
ویژگیها:
مثال:
Trial 1: [10 دایره نمایش داده میشود]
→ کاربر با تأخیر کلیک میکند (RT = 2100ms)
→ Feedback: "زمان تمام شد! سریعتر باش 😊"
Trial 2: [10 دایره]
→ کاربر سریع کلیک میکند (RT = 650ms)
→ Feedback: "عالی! 👍"
چرا مهم است؟
Learning Effect - اگر شرکتکننده در 5 trial اول داده اصلی تولید کند، نتایج مخدوش میشود.
تعداد: 50 مرحله
ویژگیها:
تولید تصادفی:
def generate_unique_trial():
"""
تولید یک آرایش تصادفی جدید
"""
positions = []
# Grid 3×3 با هر سلول 33.33% عرض/ارتفاع
grid_size = screen_height / 2 / 3 # نیمه بالایی / 3
for i in range(10):
# موقعیت تصادفی در نیمه بالایی صفحه
x = random.uniform(20, screen_width - 20)
y = random.uniform(20, screen_height / 2 - 20)
# اطمینان از عدم همپوشانی
while is_overlapping(x, y, positions):
x = random.uniform(20, screen_width - 20)
y = random.uniform(20, screen_height / 2 - 20)
positions.append({'x': x, 'y': y})
return positions
الگوریتم جلوگیری از همپوشانی:
def is_overlapping(x, y, existing_positions, min_distance=60):
"""
بررسی فاصله حداقل 60 پیکسل بین دایرهها
"""
for pos in existing_positions:
distance = sqrt((x - pos['x'])**2 + (y - pos['y'])**2)
if distance < min_distance:
return True
return False
تعداد: 15 مرحله (15 صحنه که هرکدام یکبار دیگر تکرار میشوند)
هدف اصلی:
سنجش consistency بدون آگاهی شرکتکننده
مکانیزم:
Trial 12: [صحنه A] → انتخاب: دایره #3 در گرید 7
↓
[35 trial فاصله]
↓
Trial 47: [صحنه A] → انتخاب: ؟؟؟
سوال: آیا دوباره دایره #3 در گرید 7 را انتخاب میکند؟
فواصل زمانی (Lag Distribution):
| نوع فاصله | تعداد | Trial Gap | زمان واقعی |
|---|---|---|---|
| کوتاه (Short) | 5 | 5-10 trials | 15-30 ثانیه |
| متوسط (Medium) | 5 | 15-25 trials | 45-75 ثانیه |
| بلند (Long) | 5 | 35-50 trials | 105-150 ثانیه |
چرا فواصل مختلف؟
برای سنجش decay (زوال) حافظه ضمنی:
پیشبینی:
اگر حافظه ضمنی نقش دارد:
تعداد: 5 مرحله
مکانیزم:
Trial N: [صحنه B] → انتخاب: دایره #7
Trial N+1: [صحنه B] → انتخاب: ؟؟؟
↑
بلافاصله بعد!
هدف:
سنجش تمایل به Variety Seeking (تنوعطلبی)
فرضیه رفتاری:
انسانها وقتی میدانند صحنه تکراری است، تمایل دارند گزینه متفاوتی انتخاب کنند.
مبنای نظری:
پیشبینی:
مثال واقعی:
شرکتکننده در Trial 23: دایره سمت راست-بالا را کلیک کرد
Trial 24 (همان صحنه): احتمالاً دایره دیگری را انتخاب میکند
چون "احساس تکرار" دارد
تعداد: 5 مرحله
مکانیزم:
[فقط 1 دایره در مرکز صفحه]
↓
اگر شرکتکننده آن را انتخاب نکند → غیرجدی است!
معیار حذف:
چرا لازم است؟
جایگذاری:
بهصورت تصادفی در trials 15, 30, 45, 60, 73
رنگ پاستلی یکنواخت
(بدون هیچ المان بصری)
← 10 دایره
کاربر کلیک میکند (RT ثبت میشود)
صفحه سفید خالی
مشکل طراحی:
چگونه حافظه بینایی را پاک کنیم بدون ایجاد سوگیری مکانی؟
راهحلهای رد شده:
❌ نقطه مرکزی: سوگیری به مرکز صفحه
❌ عدد شمارش معکوس: تمرکز بر یک نقطه
❌ گیف متحرک: ایجاد الگوی حرکت چشم
راهحل نهایی: Dynamic Color Field ✅
/* 15 رنگ پاستلی */
const colors = [
'#E8F4F8', // آبی روشن
'#FCE8EC', // صورتی روشن
'#FFF4E6', // نارنجی روشن
'#F0FFF4', // سبز روشن
'#EDE9FE', // بنفش روشن
'#FEF3C7', // زرد روشن
'#DBEAFE', // آبی آسمانی
'#FED7AA', // نارنجی کرم
'#E0E7FF', // یاسی روشن
'#BFDBFE', // آبی ملایم
'#FEE2E2', // قرمز روشن
'#D1FAE5', // سبز یشمی
'#F3E8FF', // بنفش پاستلی
'#FDE68A', // زرد طلایی
'#BAE6FD' // فیروزهای
]
الگوریتم انتخاب:
previous_color = None
def get_fixation_color():
global previous_color
# انتخاب تصادفی (بدون تکرار متوالی)
available = [c for c in colors if c != previous_color]
selected = random.choice(available)
previous_color = selected
return selected
مزایای علمی:
مطالعات حمایتکننده:
مشخصات بصری:
/* دایرهها */
.circle {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #6B7280; /* طوسی تیره */
cursor: pointer;
transition: transform 100ms;
}
.circle:hover {
transform: scale(1.1); /* بازخورد بصری */
}
.circle:active {
transform: scale(0.95);
}
چرا همه یکسان؟
برای حذف سوگیریهای بصری:
تصمیم طراحی: یکنواختی کامل
همه دایره → همه طوسی → همه هماندازه
Grid 3×3 برای تحلیل:
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| 7 | 8 | 9 |
مرکز صفحه (Grid 5)
تابع تبدیل:
def pixel_to_grid(x, y, screen_width=390, screen_height=844):
"""
تبدیل مختصات پیکسلی به شماره گرید (1-9)
فقط نیمه بالایی صفحه (y < 422) معتبر است
"""
if y >= screen_height / 2:
raise ValueError("انتخاب در نیمه پایینی!")
# محاسبه ستون (1, 2, 3)
col = int(x / (screen_width / 3)) + 1
# محاسبه ردیف (0, 1, 2)
row = int(y / (screen_height / 2 / 3))
# شماره گرید
grid_number = row * 3 + col
return grid_number
# مثال
pixel_to_grid(150, 100) → 1 # گوشه بالا-چپ
pixel_to_grid(195, 211) → 5 # مرکز
pixel_to_grid(350, 400) → 9 # گوشه پایین-راست (نیمه بالایی)
متن نمونه:
آزمایش تصمیمگیری سریع
هدف: بررسی الگوهای انتخاب در شرایط محدودیت زمانی
مدت زمان: حدود 5 دقیقه
روش: شما 10 دایره خواهید دید و باید سریعاً یکی را انتخاب کنید.
محرمانگی: تمام دادهها ناشناس ذخیره میشوند.
حق انصراف: در هر زمان میتوانید از آزمایش خارج شوید.
سوالات؟ ایمیل: researcher@example.com
☐ متوجه شدم و موافقم
interface Demographics {
age: number; // سن (18-45)
gender: 'male' | 'female' | 'other' | 'prefer_not_to_say';
handedness: 'right' | 'left' | 'both';
vision: 'normal' | 'corrected' | 'impaired';
education: 'high_school' | 'bachelor' | 'master' | 'phd';
device: 'android' | 'ios';
screen_size: number; // اینچ
}
چرا این اطلاعات؟
صفحه 1:
1. تعدادی دایره خواهید دید
2. سریع یکی را انتخاب کنید
3. زمان محدود است! (1.8 ثانیه)
✓ اولین انتخاب ذهنیتان را بزنید
✓ زیاد فکر نکنید!
✓ در صورت نرسیدن به موقع، مرحله تکرار میشود
ساختار JSON هر Trial:
{
"trial_id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "anonymous_abc123",
"trial_number": 15,
"trial_type": "implicit_repeat",
"timestamp": "2025-10-08T14:23:45.678Z",
"stimulus": {
"circles": [
{"x": 45.2, "y": 67.8, "id": 0},
{"x": 123.5, "y": 189.3, "id": 1},
// ... 10 دایره
],
"scene_id": "scene_042",
"is_repeat": true,
"original_trial": 8,
"lag": 7
},
"response": {
"selected_circle_id": 3,
"grid_position": 5,
"rt_ms": 847,
"mouse_path": [[0,100], [50,150], ...], // اختیاری
"timeout": false
},
"context": {
"fixation_color": "#E8F4F8",
"block_number": 2,
"time_of_day": "afternoon"
}
}
تعریف:
محاسبه برای هر فرد:
def calculate_CI(user_data):
"""
محاسبه Consistency Index
"""
repeats = user_data[user_data['is_repeat'] == True]
matches = 0
total = 0
for _, repeat in repeats.iterrows():
original_trial = user_data[
user_data['scene_id'] == repeat['scene_id']
].iloc[0]
if repeat['selected_circle_id'] == original_trial['selected_circle_id']:
matches += 1
total += 1
CI = (matches / total) * 100
return CI
# مثال
user_001: 12 تکرار → 4 یکسان → CI = 33.3%
user_002: 12 تکرار → 1 یکسان → CI = 8.3%
تفسیر:
تعریف:
زمان از نمایش محرک تا کلیک (میلیثانیه)
محاسبه:
// کد فرانتاند
const stimulusOnset = performance.now();
// ... کاربر کلیک میکند ...
const clickTime = performance.now();
const RT = clickTime - stimulusOnset; // ms
آنالیز آماری:
import pandas as pd
import scipy.stats as stats
# حذف outliers (RT < 200ms یا RT > 1800ms)
df_clean = df[(df['rt'] >= 200) & (df['rt'] <= 1800)]
# مقایسه
rt_repeated = df_clean[df_clean['is_repeat'] == True]['rt']
rt_unique = df_clean[df_clean['is_repeat'] == False]['rt']
# t-test
t_stat, p_value = stats.ttest_ind(rt_repeated, rt_unique)
# Effect size (Cohen's d)
pooled_std = np.sqrt(
(rt_repeated.std()**2 + rt_unique.std()**2) / 2
)
cohen_d = (rt_repeated.mean() - rt_unique.mean()) / pooled_std
فرضیه:
اگر و : حافظه ضمنی وجود دارد!
تعریف - آنتروپی شانون:
با:
محاسبه:
import numpy as np
def calculate_entropy(choices):
"""
محاسبه آنتروپی فضایی
choices: لیست شماره گریدها [5, 2, 5, 7, 1, ...]
"""
# شمارش فراوانی
counts = np.bincount(choices, minlength=10)[1:10] # گریدهای 1-9
# احتمالات
probabilities = counts / counts.sum()
# حذف صفرها (log(0) تعریف نشده)
probabilities = probabilities[probabilities > 0]
# آنتروپی
H = -np.sum(probabilities * np.log2(probabilities))
return H
# مثال
choices_uniform = [1,2,3,4,5,6,7,8,9] * 10 # کاملاً یکنواخت
H_uniform = calculate_entropy(choices_uniform)
# H = 3.17 bits (maximum)
choices_biased = [5] * 50 + [1,2,3,4,6,7,8,9] * 5 # سوگیری به مرکز
H_biased = calculate_entropy(choices_biased)
# H ≈ 2.1 bits
تفسیر:
| Entropy | تفسیر | مثال |
|---|---|---|
| 3.17 | یکنواخت کامل | |
| 2.5-3.0 | سوگیری ضعیف | مرکز کمی بیشتر |
| 1.5-2.5 | سوگیری متوسط | 2-3 گرید غالب |
| < 1.5 | سوگیری شدید | تقریباً همیشه یک گرید |
دو گروه با شمارهگذاری معکوس:
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| 7 | 8 | 9 |
| 3 | 2 | 1 |
| 6 | 5 | 4 |
| 9 | 8 | 7 |
تحلیل ترکیبی:
def mirror_grid(grid_number, group):
"""
تبدیل شماره گرید به فضای یکسان
"""
if group == 'B':
mirror_map = {1:3, 2:2, 3:1, 4:6, 5:5, 6:4, 7:9, 8:8, 9:7}
return mirror_map[grid_number]
return grid_number
# در آنالیز نهایی
df['normalized_grid'] = df.apply(
lambda row: mirror_grid(row['grid'], row['group']),
axis=1
)
هدف: جداسازی "سوگیری واقعی" از "artifact طراحی"
def get_time_category(timestamp):
hour = timestamp.hour
if 6 <= hour < 12:
return 'morning'
elif 12 <= hour < 18:
return 'afternoon'
elif 18 <= hour < 24:
return 'evening'
else:
return 'night'
چرا مهم است?
Circadian rhythm - تحقیقات نشان دادهاند که:
کنترل آماری:
# ANCOVA با زمان روز بهعنوان covariate
from statsmodels.formula.api import ols
model = ols('CI ~ C(trial_type) + C(time_of_day)', data=df).fit()
import pandas as pd
import numpy as np
def describe_sample(df):
"""
آمار توصیفی کلی
"""
stats = {
'CI': {
'mean': df.groupby('user_id')['CI'].first().mean(),
'std': df.groupby('user_id')['CI'].first().std(),
'median': df.groupby('user_id')['CI'].first().median(),
'range': (
df.groupby('user_id')['CI'].first().min(),
df.groupby('user_id')['CI'].first().max()
)
},
'RT': {
'mean': df['rt'].mean(),
'std': df['rt'].std(),
'median': df['rt'].median()
},
'Entropy': {
'mean': df.groupby('user_id')['entropy'].first().mean(),
'std': df.groupby('user_id')['entropy'].first().std()
}
}
return stats
# خروجی نمونه
"""
Consistency Index (CI):
Mean = 18.7% (SD = 12.3%)
Median = 16.7%
Range = [0%, 53.3%]
Reaction Time (RT):
Mean = 847ms (SD = 203ms)
Median = 812ms
Spatial Entropy (H):
Mean = 2.89 bits (SD = 0.34)
"""
فرضیه:
محاسبه:
from scipy.stats import binomtest
# برای هر شرکتکننده
user = df[df['user_id'] == 'user_001']
repeats = user[user['is_repeat'] == True]
n_trials = len(repeats) # مثلاً 15
n_matches = (repeats['is_match'] == True).sum() # مثلاً 6
# Binomial test (one-tailed)
result = binomtest(
k=n_matches,
n=n_trials,
p=0.10,
alternative='greater'
)
print(f"p-value = {result.pvalue}")
# مثال:
# 6 موفقیت از 15 → p-value = 0.0012 ⭐
# پس CI این فرد معنادار است!
تحلیل گروهی:
# تعداد افرادی که CI معنادار دارند
significant_users = 0
for user_id in df['user_id'].unique():
user_data = df[df['user_id'] == user_id]
# ... محاسبه p-value ...
if p_value < 0.05:
significant_users += 1
proportion = significant_users / len(df['user_id'].unique())
print(f"{proportion*100:.1f}% افراد CI معنادار دارند")
# پیشبینی: اگر > 50% → فرضیه تأیید میشود
from scipy.stats import ttest_rel
# برای هر شرکتکننده
user = df[df['user_id'] == 'user_001']
# RT در دفعه اول دیدن صحنه
first_encounters = user[
(user['is_repeat'] == False) |
(user['is_original'] == True)
]['rt']
# RT در تکرارها
repeats = user[
(user['is_repeat'] == True) &
(user['is_original'] == False)
]['rt']
# t-test
t_stat, p_value = ttest_rel(first_encounters, repeats)
# Effect size
d = (repeats.mean() - first_encounters.mean()) / np.std(first_encounters - repeats)
print(f"t({len(first_encounters)-1}) = {t_stat:.2f}, p = {p_value:.4f}, d = {d:.2f}")
# مثال خروجی:
# t(14) = -2.31, p = 0.036, d = -0.42
# تفسیر: RT در تکرارها 42ms کمتر است (معنادار)
نمودار Box Plot:
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(8, 6))
sns.boxplot(data=[first_encounters, repeats])
plt.xticks([0, 1], ['اولین مواجهه', 'تکرار'])
plt.ylabel('زمان واکنش (ms)')
plt.title('مقایسه RT در مراحل اول و تکرار')
plt.show()
فرضیه:
from scipy.stats import chisquare
# فراوانی مشاهدهشده
observed = df['grid'].value_counts().sort_index().values
# فراوانی مورد انتظار (یکنواخت)
expected = [len(df) / 9] * 9
# Chi-square test
chi2, p_value = chisquare(observed, expected)
print(f"χ²(8) = {chi2:.2f}, p = {p_value:.4f}")
# اگر p < 0.05 → توزیع غیریکنواخت است
Heatmap سوگیری:
import seaborn as sns
import matplotlib.pyplot as plt
# محاسبه نسبتها
grid_counts = df['grid'].value_counts()
grid_proportions = grid_counts / grid_counts.sum()
# تبدیل به ماتریس 3×3
heatmap_data = np.array([
[grid_proportions.get(1, 0), grid_proportions.get(2, 0), grid_proportions.get(3, 0)],
[grid_proportions.get(4, 0), grid_proportions.get(5, 0), grid_proportions.get(6, 0)],
[grid_proportions.get(7, 0), grid_proportions.get(8, 0), grid_proportions.get(9, 0)]
])
# رسم نمودار
plt.figure(figsize=(8, 6))
sns.heatmap(
heatmap_data * 100, # به درصد
annot=True,
fmt='.1f',
cmap='YlOrRd',
cbar_kws={'label': 'درصد انتخاب'},
vmin=8, # حداقل: کمتر از میانگین
vmax=15 # حداکثر: بیشتر از میانگین
)
plt.title('نقشه حرارتی سوگیری مکانی')
plt.xlabel('ستون')
plt.ylabel('ردیف')
plt.show()
# خروجی نمونه:
نقشه حرارتی سوگیری مکانی (خروجی نمونه):
9.2%
13.1%
8.7%
11.5%
17.8%
10.3%
8.9%
12.4%
8.1%
کمتر از میانگین
نزدیک میانگین
بیشترین انتخاب (مرکز)
from scipy.stats import f_oneway
# گروهبندی بر اساس lag
short_lag = df[df['lag_category'] == 'short']['is_match']
medium_lag = df[df['lag_category'] == 'medium']['is_match']
long_lag = df[df['lag_category'] == 'long']['is_match']
# ANOVA
F, p_value = f_oneway(short_lag, medium_lag, long_lag)
print(f"F(2, {len(df)-3}) = {F:.2f}, p = {p_value:.4f}")
# Post-hoc: Tukey HSD
from statsmodels.stats.multicomp import pairwise_tukeyhsd
tukey = pairwise_tukeyhsd(
endog=df['is_match'],
groups=df['lag_category'],
alpha=0.05
)
print(tukey)
نمودار خط روند:
import matplotlib.pyplot as plt
lag_means = df.groupby('lag_category')['CI'].mean()
plt.figure(figsize=(10, 6))
plt.plot(['کوتاه', 'متوسط', 'بلند'], lag_means, marker='o', linewidth=2)
plt.xlabel('فاصله زمانی')
plt.ylabel('Consistency Index (%)')
plt.title('تأثیر فاصله زمانی بر همسانی انتخاب')
plt.grid(True, alpha=0.3)
plt.show()
# پیشبینی: نمودار نزولی (فراموشی زمانی)
مدل پیشبینی:
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
# آمادهسازی داده
X = df[['rt_first', 'lag', 'is_center_grid']]
y = df['is_match']
# نرمالسازی
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# مدل
model = LogisticRegression()
model.fit(X_scaled, y)
# ضرایب
print("ضرایب:")
print(f" RT اول: {model.coef_[0][0]:.3f}")
print(f" Lag: {model.coef_[0][1]:.3f}")
print(f" مرکز: {model.coef_[0][2]:.3f}")
# Odds Ratios
odds_ratios = np.exp(model.coef_[0])
print("\nOdds Ratios:")
print(f" RT اول: {odds_ratios[0]:.2f}")
print(f" Lag: {odds_ratios[1]:.2f}")
print(f" مرکز: {odds_ratios[2]:.2f}")
# تفسیر:
# اگر OR(مرکز) = 1.85 → انتخاب مرکزی 85% بیشتر تکرار میشود
مشکل:
شرکتکننده در طول آزمایش یاد میگیرد "چگونه بازی کند"
کنترل:
# اضافه کردن trial_number به مدل
model = ols('CI ~ trial_number + trial_type', data=df).fit()
مشکل:
در پایان آزمایش، توجه کاهش مییابد
شواهد:
# مقایسه RT در بلوکهای مختلف
early = df[df['block'] == 'early']['rt'].mean()
late = df[df['block'] == 'late']['rt'].mean()
if late > early * 1.2: # 20% افزایش
print("⚠️ نشانههای خستگی مشاهده میشود")
کنترل:
# تحلیل با/بدون 10 trial آخر
results_full = analyze_data(df)
results_trimmed = analyze_data(df[df['trial_number'] <= 65])
if results_full['CI'] ≈ results_trimmed['CI']:
print("✅ نتایج پایدار هستند")
مشکل:
دیدن یک محرک، پردازش محرک بعدی را تحت تأثیر قرار میدهد
مثال:
اگر trial قبلی گوشه بالا-راست بود، ممکن است trial بعدی هم همانجا!
کنترل:
def check_sequential_dependency(df):
"""
بررسی وابستگی متوالی
"""
df['prev_grid'] = df.groupby('user_id')['grid'].shift(1)
# آیا انتخاب فعلی به قبلی وابسته است؟
contingency_table = pd.crosstab(df['prev_grid'], df['grid'])
chi2, p_value = chi2_contingency(contingency_table)
if p_value < 0.05:
print("⚠️ وابستگی متوالی وجود دارد!")
return p_value
راهحل:
Fixation screen با رنگهای مختلف این اثر را کاهش میدهد.
مشکل:
آیا نتایج به کامپیوتر/تبلت تعمیم مییابد؟
پاسخ:
بله، چون:
اما:
Mouse بهتر از touch دنبال میشود → ثبت میکنیم و کنترل میکنیم
مشکل:
شرکتکنندگان داوطلب هستند → ممکن است motivated باشند
کنترل:
تهدیدها:
کنترل:
✅ مقایسه Implicit vs Explicit:
CI_implicit = calculate_CI(df[df['repeat_type'] == 'implicit'])
CI_explicit = calculate_CI(df[df['repeat_type'] == 'explicit'])
if CI_implicit > CI_explicit:
print("✅ CI ناخودآگاه است (نه عمدی)")
شرکتکننده باید بداند:
نمونه متن:
"در این مطالعه، الگوهای تصمیمگیری سریع را بررسی میکنیم.
شما باید دایرهها را سریع انتخاب کنید.
تمام دادهها ناشناس است و میتوانید در هر زمان خارج شوید."
# تولید شناسه ناشناس
import uuid
user_id = str(uuid.uuid4()) # مثلاً: "550e8400-e29b-41d4-..."
# هیچ اطلاعات شخصیساز (PII) ذخیره نمیشود:
# ❌ نام
# ❌ ایمیل
# ❌ IP address
# ❌ شماره تلفن
# فقط:
# ✅ سن (بازه)
# ✅ جنسیت
# ✅ تحصیلات
// ارتباط رمزنگاریشده
const API_URL = 'https://api.example.com'; // HTTPS فقط
// ذخیرهسازی رمزشده
const encryptData = (data) => {
// استفاده از AES-256
return CryptoJS.AES.encrypt(JSON.stringify(data), SECRET_KEY);
};
مدت نگهداری:
این آزمایش بررسی میکرد که آیا در شرایط یکسان، تصمیمات یکسانی میگیریم.
برخی صحنهها تکراری بودند تا ببینیم آیا همان انتخاب را میکنید.
| 📧 سوال؟ | ایمیل: support@shivatek.ir |
| ID شما |
XYZ123
(برای پیگیری)
|
چرا مهم است؟
این مطالعه باید قبل از اجرا، توسط یک کمیته اخلاق (IRB - Institutional Review Board) تأیید شود.
معیارهای تأیید:
نتیجه:
Mean CI = 11.2% (SD = 8.3%)
p-value = 0.43 (ns)
تفسیر:
✅ تصمیمگیری در شرایط محدود زمانی، کاملاً تصادفی است
✅ حافظه ضمنی نقشی ندارد
✅ انسان هر بار "از نو" تصمیم میگیرد
پیامدهای نظری:
نتیجه:
Mean CI = 24.7% (SD = 11.2%)
p-value = 0.002** (معنادار)
تفسیر:
⚠️ الگویی وجود دارد، اما ضعیف
⚠️ احتمالاً ترکیبی از:
آنالیز تکمیلی:
# تجزیه CI به اجزا
CI_total = 24.7%
# اگر فقط به bias مکانی مربوط باشد:
CI_expected_from_bias = calculate_bias_contribution()
# مثلاً 8%
# پس باقیمانده:
CI_memory = CI_total - CI_expected_from_bias
# = 16.7% → حافظه ضمنی!
نتیجه:
Mean CI = 42.3% (SD = 15.8%)
p-value < 0.001*** (بسیار معنادار)
تفسیر:
⚡ تصمیمات بسیار deterministic هستند
⚡ حتی بدون آگاهی، مغز الگو میسازد
فرضیههای توضیحی:
# تأثیر معیارهای مختلف
sensitivity_results = {}
# معیار 1: فاصله زمانی
for lag in ['short', 'medium', 'long']:
CI_lag = calculate_CI(df[df['lag_category'] == lag])
sensitivity_results[f'CI_{lag}'] = CI_lag
# معیار 2: موقعیت
for grid in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
CI_grid = calculate_CI(df[df['grid'] == grid])
sensitivity_results[f'CI_grid_{grid}'] = CI_grid
# معیار 3: زمان واکنش
RT_fast = df[df['rt'] < df['rt'].median()]
RT_slow = df[df['rt'] >= df['rt'].median()]
sensitivity_results['CI_fast_RT'] = calculate_CI(RT_fast)
sensitivity_results['CI_slow_RT'] = calculate_CI(RT_slow)
# نمودار
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
plt.bar(range(len(sensitivity_results)), list(sensitivity_results.values()))
plt.xticks(range(len(sensitivity_results)), list(sensitivity_results.keys()), rotation=45)
plt.ylabel('Consistency Index (%)')
plt.title('تحلیل حساسیت')
plt.axhline(y=10, color='r', linestyle='--', label='شانس محض')
plt.legend()
plt.tight_layout()
plt.show()
عدم حذف کامل حافظه:
تعداد محدود گزینهها:
محیط مصنوعی:
حجم نمونه:
تعمیمپذیری:
مطالعه 1: تأثیر پاداش
شرایط A: بدون پاداش (مانند فعلی)
شرایط B: +10 امتیاز برای انتخاب سریع
شرایط C: -5 امتیاز برای timeout
فرضیه: پاداش → کاهش CI (تمرکز بر سرعت، نه الگو)
مطالعه 2: تصمیمگیری اجتماعی
فاز 1: فرد تنها (مانند فعلی)
فاز 2: مشاهده انتخاب فرد دیگر قبل از تصمیم
فرضیه: Social influence → افزایش CI (تقلید)
مطالعه 3: دستکاری حالت شناختی
Group A: استراحت کافی
Group B: محرومیت از خواب (24h)
Group C: استرس (Trier Social Stress Test)
فرضیه: خستگی/استرس → کاهش CI (افزایش randomness)
EEG (نوار مغز):
سوال: آیا Readiness Potential (RP) در تکرارها زودتر شروع میشود؟
روش:
پیشبینی: اگر حافظه ضمنی وجود دارد → RP زودتر
fMRI (تصویربرداری عملکردی):
مناطق مورد علاقه:
فرضیه:
Drift Diffusion Model (DDM):
from hddm import HDDM
# پارامترها:
# v: drift rate (سرعت تصمیم)
# a: threshold (محتاطبودن)
# t: non-decision time (زمان غیرتصمیمی)
model = HDDM(data, depends_on={'v': 'is_repeat'})
model.sample(5000, burn=1000)
# سوال: آیا v در تکرارها بیشتر است؟
Bayesian Inference:
# مدل احتمالاتی
import pymc3 as pm
with pm.Model() as model:
# Prior
p_match = pm.Beta('p_match', alpha=2, beta=18) # prior: نزدیک به 10%
# Likelihood
matches = pm.Binomial('matches', n=15, p=p_match, observed=observed_data)
# Posterior
trace = pm.sample(2000)
# نتیجه: توزیع پسین p_match
Libet, B., Gleason, C. A., Wright, E. W., & Pearl, D. K. (1983).
Time of conscious intention to act in relation to onset of cerebral activity (readiness-potential): The unconscious initiation of a freely voluntary act.
Brain, 106(3), 623-642.
DOI: 10.1093/brain/106.3.623
Kahneman, D. (2011).
Thinking, Fast and Slow.
Farrar, Straus and Giroux.
ISBN: 978-0374533557
Squire, L. R. (1992).
Declarative and nondeclarative memory: Multiple brain systems supporting learning and memory.
Journal of Cognitive Neuroscience, 4(3), 232-243.
DOI: 10.1162/jocn.1992.4.3.232
Engbert, R., & Kliegl, R. (2003).
Microsaccades uncover the orientation of covert attention.
Vision Research, 43(9), 1035-1045.
DOI: 10.1016/S0042-6989(03)00084-1
Theeuwes, J., Kramer, A. F., Hahn, S., & Irwin, D. E. (1998).
Our eyes do not always go where we want them to go: Capture of the eyes by new objects.
Psychological Science, 9(5), 379-385.
DOI: 10.1111/1467-9280.00071
Field, A. (2013).
Discovering Statistics Using IBM SPSS Statistics (4th ed.).
SAGE Publications.
ISBN: 978-1446249178
Ratcliff, R., & McKoon, G. (2008).
The diffusion decision model: Theory and data for two-choice decision tasks.
Neural Computation, 20(4), 873-922.
DOI: 10.1162/neco.2008.12-06-420
def calculate_consistency_index(user_data):
"""
محاسبه Consistency Index برای یک شرکتکننده
Parameters:
-----------
user_data : DataFrame
دادههای یک کاربر با ستونهای:
- trial_number
- scene_id
- selected_circle_id
- is_repeat
- original_trial_number
Returns:
--------
dict: {
'CI': float,
'n_matches': int,
'n_repeats': int,
'p_value': float
}
"""
from scipy.stats import binomtest
# فیلتر تکرارها
repeats = user_data[user_data['is_repeat'] == True].copy()
matches = 0
total = len(repeats)
for _, repeat_row in repeats.iterrows():
# پیدا کردن trial اصلی
original = user_data[
user_data['trial_number'] == repeat_row['original_trial_number']
].iloc[0]
# بررسی تطابق
if repeat_row['selected_circle_id'] == original['selected_circle_id']:
matches += 1
# محاسبه CI
CI = (matches / total * 100) if total > 0 else 0
# آزمون آماری
binom_result = binomtest(
k=matches,
n=total,
p=0.10,
alternative='greater'
)
return {
'CI': CI,
'n_matches': matches,
'n_repeats': total,
'p_value': binom_result.pvalue
}
# مثال استفاده
# user_001_data = df[df['user_id'] == 'user_001']
# result = calculate_consistency_index(user_001_data)
# print(f"CI = {result['CI']:.1f}%, p = {result['p_value']:.4f}")
import numpy as np
p = 0.247 # مثلاً CI = 24.7%
n = 15 # تعداد تکرارها
SE = np.sqrt(p * (1 - p) / n)
# SE ≈ 0.111 = 11.1%
# Confidence Interval 95%
CI_lower = p - 1.96 * SE
CI_upper = p + 1.96 * SE
print(f"CI: {CI_lower*100:.1f}% - {CI_upper*100:.1f}%")
# خروجی: CI: 3.0% - 46.4%
$$s_{\text{pooled}} = \sqrt{\frac{(n_1-1)s_1^2 + (n_2-1)s_2^2}{n_1 + n_2 - 2
👌 من کل طرح آزمایش را با همه جزئیات و کدها مرور کردم — کاملاً جامع، مستند و استاندارد است. این پروتکل از نظر علمی و آماری بهخوبی طراحی شده و تمام کنترلهای لازم (حافظه، سوگیری، زمان، اخلاق) رعایت شده است.
پیشنهاد من:
Pilot Study
ابتدا نسخه کوتاه (مثلاً 30 مرحله) را روی 8–10 نفر اجرا کنید تا مطمئن شوید تایمینگ و رابط کاربری بدون خطا کار میکند و RTها منطقی هستند.
Validation Checks
بعد از پایلوت، نرخ موفقیت در Catch Trials را بررسی کنید تا حدود ≥90% باشد (نشانه تعامل جدی).
Data Logging Precision
در PWA، برای ثبت زمانها از performance.now() استفاده کنید (به جای Date.now())، چون دقت میلیثانیهای دارد.
Stats Pre-registration
فرضیهها، شاخصها، روش تحلیل و آستانههای معناداری را در OSF یا مشابه آن قبل از جمعآوری دادهها ثبت کنید تا از خطاهای p-hacking جلوگیری شود.
Device Variability
حتماً مدل و سیستمعامل دستگاه را ذخیره کنید تا بتوانید در تحلیل، اثر احتمالی سختافزار/تاچ را کنترل کنید.
اگر بخواهید، میتوانم یک نسخه مینیمال اجرایی PWA از این پروتکل برای پایلوت آماده کنم که شامل: