# Data Analysis Reproducibility Document

**Manuscript:** Response styles and value internalisation in entrepreneurship
education evaluation: a survey of 16,075 Chinese students

**Target journal:** Humanities and Social Sciences Communications (HSSC)

**Data file:** `06_Data_Deidentified_reviewer.csv` (16,075 rows × 46 columns)

**Codebook:** `06_Data_codebook.md`

---

## 1. Overview

This document provides all information needed to reproduce the descriptive and
inferential statistics reported in the manuscript. All analyses were conducted
in Python 3.13 using pandas, numpy, scipy, scikit-learn, statsmodels, and
semopy. The de-identified dataset contains all variables necessary for
reproduction; no external data sources are required.

### 1.1 Software environment

| Package | Version | Purpose |
|---|---|---|
| Python | 3.13 | Runtime |
| pandas | 3.0.5 | Data manipulation |
| numpy | 2.x | Numerical computation |
| scipy | 1.18.0 | Statistical tests (t-test, chi-square) |
| scikit-learn | 1.9.0 | Gaussian mixture models (LPA) |
| statsmodels | 0.14.6 | Multinomial logistic regression |
| semopy | 2.3.11 | CFA, bifactor model, path model |

### 1.2 Data provenance and de-identification

The original survey was conducted in March–June 2022 through the Wenjuanxing
platform. The original data file contains institution names and Chinese-language
categorical labels. For reviewer access, the following de-identification steps
were applied:

1. Institution names → `tier_code` (1–4) + `elite` (0/1)
2. Chinese categorical labels → numeric codes (`region_code`, `year_code`,
   `discipline_code`, `material_code`)
3. No direct identifiers (name, student ID, phone, e-mail) were ever collected
4. No rows were dropped, merged, or perturbed; item-level responses are
   unchanged from the cleaned analysis file
5. `VR_mean`, `IR_mean`, `VR_IR_diff`, `VR_dominant` were recomputed from the
   25 raw items and verified against the analysis file (16,075/16,075 exact
   match)
6. `profile` (LPA class 1–6) was merged from the analysis file; row alignment
   verified (0 mismatches on shared variables)

---

## 2. Variable definitions

### 2.1 Scale composition

| Dimension | Items (questionnaire order) | Count |
|---|---|---|
| Value orientation (VR) | item_1, item_3, item_5, item_6, item_7, item_12, item_14, item_15, item_17, item_19, item_22, item_23, item_24, item_25 | 14 |
| Instrumental orientation (IR) | item_2, item_4, item_8, item_9, item_10, item_11, item_13, item_16, item_18, item_20, item_21 | 11 |

### 2.2 Sample definitions

| Sample | N | Definition |
|---|---|---|
| Full valid sample | 16,075 | No missing values on orientation items, satisfaction, course-taking, institutional type, gender, or place of origin |
| Path analysis sample | 14,284 | Full sample excluding 1,791 respondents with missing perceived climate |
| Exclusion robustness sample | 13,374 | Path sample excluding 910 full-endorsing responders (1,022 in full sample, of whom 112 had missing climate) |

### 2.3 Key derived variables

| Variable | Definition |
|---|---|
| `VR_mean` | Mean of 14 value-orientation items |
| `IR_mean` | Mean of 11 instrumental-orientation items |
| `full_endorsing` | All 25 items rated 5 (computed: `(df[item_cols] == 5).all(axis=1)`) |
| `vr5` | VR_mean ≥ 4.999999 (i.e., all 14 VR items rated 5) |
| `ir5` | IR_mean ≥ 4.999999 |
| Response-style tier | T1: all 25 items = 5; T2: ≥ 1 item = 5 but not all; T3: 0 items = 5 |
| Four-quadrant | VR_mean > median (4.50) = high; IR_mean > median (3.18) = high |

---

## 3. Reproduction code

### 3.1 Loading and preparation

```python
import pandas as pd
import numpy as np
from scipy import stats
from scipy.stats import chi2_contingency

df = pd.read_csv('06_Data_Deidentified_reviewer.csv')
N = len(df)  # 16,075

VR_items = [1, 3, 5, 6, 7, 12, 14, 15, 17, 19, 22, 23, 24, 25]
IR_items = [2, 4, 8, 9, 10, 11, 13, 16, 18, 20, 21]
VR_cols = [f'item_{i}' for i in VR_items]
IR_cols = [f'item_{i}' for i in IR_items]
all_items = [f'item_{i}' for i in range(1, 26)]

path_df = df[df['climate'].notna()]  # N = 14,284
full_endorsing_mask = (df[all_items] == 5).all(axis=1)  # n = 1,022
```

### 3.2 Table 1: Sample characteristics

```python
# Gender: 1=female, 0=male
male = (df['gender'] == 0).sum()      # 6,727 (41.8%)
female = (df['gender'] == 1).sum()    # 9,348 (58.2%)

# Age
age = df['age'].dropna()
age.mean()  # 20.1; age.std() = 2.69; missing = 284

# Origin
(df['origin'] == 'urban').sum()  # 5,670 (35.3%)

# Institutional type
(df['tier_code'] == 1).sum()  # 424 (DFC)
(df['tier_code'] == 2).sum()  # 983 (Provincial DFC)
(df['tier_code'] == 3).sum()  # 14,392 (Regular)
(df['tier_code'] == 4).sum()  # 276 (Vocational)
df['elite'].sum()             # 1,407 (8.8%)
```

### 3.3 Section 3.2: Scale statistics

```python
def cronbach_alpha(data):
    k = data.shape[1]
    var_sum = data.var(axis=0, ddof=1).sum()
    total_var = data.sum(axis=1).var(ddof=1)
    return (k / (k - 1)) * (1 - var_sum / total_var)

cronbach_alpha(df[VR_cols])  # 0.955
cronbach_alpha(df[IR_cols])  # 0.865
df['VR_mean'].mean()         # 4.35
df['VR_mean'].std()          # 0.63
df['IR_mean'].mean()         # 3.33
df['IR_mean'].std()          # 0.70
df['VR_mean'].corr(df['IR_mean'])  # 0.26
df['satisfaction'].mean()    # 3.54
df['satisfaction'].std()     # 0.89
df['climate'].mean()         # 3.52
df['climate'].std()          # 0.85
```

### 3.4 Table 3: LPA profile statistics

```python
for p in range(1, 7):
    pdata = df[df['profile'] == p]
    print(f"P{p}: n={len(pdata)}, VR={pdata['VR_mean'].mean():.2f}, "
          f"IR={pdata['IR_mean'].mean():.2f}, "
          f"Sat={pdata['satisfaction'].mean():.2f}, "
          f"Climate={pdata['climate'].mean():.2f}, "
          f"Courses={pdata['courses'].mean():.2f}, "
          f"Elite%={pdata['elite'].mean()*100:.1f}")
```

Profile sizes (Table 3): P1–P6 = 1,022 / 1,864 / 5,196 / 4,416 / 1,689 / 1,888.

### 3.5 Table 5: Institution comparisons

```python
elite = df[df['elite'] == 1]
ordinary = df[df['elite'] == 0]

# Welch t-test + Cohen's d (sample-size weighted pooled SD)
def welch_d(g1, g2):
    t, p = stats.ttest_ind(g1.dropna(), g2.dropna(), equal_var=False)
    n1, n2 = len(g1), len(g2)
    s1, s2 = g1.std(ddof=1), g2.std(ddof=1)
    pooled_sd = np.sqrt(((n1-1)*s1**2 + (n2-1)*s2**2) / (n1+n2-2))
    d = (g1.mean() - g2.mean()) / pooled_sd
    return t, p, d

welch_d(elite['satisfaction'], ordinary['satisfaction'])
# t=3.88, p<.001, d=0.13

welch_d(path_df[path_df['elite']==1]['climate'],
        path_df[path_df['elite']==0]['climate'])
# t=7.85, p<.001, d=0.26

welch_d(elite['VR_mean'], ordinary['VR_mean'])
# t=-3.23, p=.001, d=-0.11

welch_d(elite['IR_mean'], ordinary['IR_mean'])
# t=6.53, p<.001, d=0.21
```

### 3.6 Section 4.3: Full-endorsing composition

```python
vr5_mask = df['VR_mean'] >= 4.999999
vr5_n = vr5_mask.sum()  # 2,886

# Among VR=5: full-endorsing vs value-endorsing
fe_vr5 = (full_endorsing_mask & vr5_mask).sum()  # 1,022 (35.4%)
ve_vr5 = vr5_n - fe_vr5                           # 1,864 (64.6%)

# By institution
elite_vr5 = ((df['elite']==1) & vr5_mask).sum()           # 336
elite_vr5_fe = ((df['elite']==1) & vr5_mask & full_endorsing_mask).sum()
elite_vr5_fe / elite_vr5 * 100  # 52.1%

nonelite_vr5 = ((df['elite']==0) & vr5_mask).sum()        # 2,550
nonelite_vr5_fe = ((df['elite']==0) & vr5_mask & full_endorsing_mask).sum()
nonelite_vr5_fe / nonelite_vr5 * 100  # 33.2%

# Chi-square
chi2, _, _, _ = chi2_contingency([
    [elite_vr5_fe, elite_vr5 - elite_vr5_fe],
    [nonelite_vr5_fe, nonelite_vr5 - nonelite_vr5_fe]
])  # chi2 = 45.4, p < .001

# IR=5
ir5_mask = df['IR_mean'] >= 4.999999
ir5_n = ir5_mask.sum()  # 1,057
ir5_fe = (ir5_mask & full_endorsing_mask).sum()
ir5_fe / ir5_n * 100  # 96.7%
```

### 3.7 Table 8: Dose-response

```python
for c in [0, 1, 2, 3]:
    cdata = df[df['courses'] == c]
    print(f"Courses={c}: n={len(cdata)}, "
          f"VR={cdata['VR_mean'].mean():.3f}, "
          f"IR={cdata['IR_mean'].mean():.3f}")

# Courses 0/1/2/3: n = 4,526 / 6,800 / 2,391 / 2,358
# VR = 4.290 / 4.346 / 4.371 / 4.465; IR = 3.394 / 3.296 / 3.306 / 3.335

df['VR_mean'].corr(df['courses'])  # r = 0.086
df['IR_mean'].corr(df['courses'])  # r = -0.029

# After excluding full-endorsing
df_excl = df[~full_endorsing_mask]
df_excl['VR_mean'].corr(df_excl['courses'])  # r = 0.098
```

### 3.8 Table 9: Four-quadrant and response-style tiers

```python
vr_median = df['VR_mean'].median()  # 4.50
ir_median = df['IR_mean'].median()  # 3.18

vr_high = df['VR_mean'] > vr_median
ir_high = df['IR_mean'] > ir_median

# Quadrant percentages
(vr_high & ir_high).sum() / N * 100       # Q1: 23.6%
(vr_high & ~ir_high).sum() / N * 100      # Q2: 23.8%
(~vr_high & ir_high).sum() / N * 100      # Q3: 23.8%
(~vr_high & ~ir_high).sum() / N * 100     # Q4: 28.8%

# Response-style tiers
max_items = (df[all_items] == 5).sum(axis=1)
t1 = max_items == 25                        # 1,022 (6.4%)
t2 = (max_items >= 1) & (max_items < 25)   # 11,766 (73.2%)
t3 = max_items == 0                         # 3,287 (20.4%)
```

---

## 4. Verification results

A comprehensive verification script (`_verify_final.py`) was run against the
de-identified data file. The script checks 154 individual data points from the
manuscript against recomputed values.

### 4.1 Summary

| Section | Checks | PASS | FAIL |
|---|---|---|---|
| 1. Sample characteristics (Table 1) | 28 | 28 | 0 |
| 2. Scale statistics (Section 3.2) | 11 | 11 | 0 |
| 3. LPA profiles (Table 3) | 48 | 48 | 0 |
| 4. Institution comparisons (Table 5) | 16 | 16 | 0 |
| 5. Full-endorsing composition (Section 4.3) | 14 | 14 | 0 |
| 6. Dose-response (Table 8) | 15 | 15 | 0 |
| 7. Four-quadrant (Table 9a) | 11 | 11 | 0 |
| 8. Response-style tiers (Table 9b) | 10 | 10 | 0 |
| **Total** | **154** | **154** | **0** |

### 4.2 Note on Cohen's d

Cohen's d is computed using the sample-size weighted pooled SD formula:

d = (M₁ − M₂) / √[((n₁−1)s₁² + (n₂−1)s₂²) / (n₁+n₂−2)]

This is the standard formula used in most statistical software. All four
d values in Table 5 (satisfaction d = 0.13, climate d = 0.26, VR d = −0.11,
IR d = 0.21) match exactly with this formula.

### 4.3 Items NOT verified from this dataset

The following analyses require model fitting and cannot be fully reproduced
from the de-identified CSV alone, as they depend on estimation algorithms with
stochastic elements:

- **Table 2 (CFA/bifactor model fit):** CFI, TLI, RMSEA, ECV decomposition —
  requires semopy model estimation
- **Table 4 (Multinomial logistic regression):** OR and 95% CI — requires
  statsmodels MNLogit estimation
- **Table 6 (Path model coefficients):** β coefficients and between-group p —
  requires semopy path model estimation
- **Table 7 (Mediation decomposition):** Indirect effect percentages —
  requires semopy path model with mediation decomposition
- **Supplementary Table S1 (LPA model comparison):** BIC, aBIC, entropy for
  k = 2–6 — requires sklearn GaussianMixture refitting

All descriptive statistics, profile assignments, and derived variables in the
dataset are fully reproducible and have been verified. The model-based results
(CFA fit indices, path coefficients, ORs) were computed from the same dataset
using the code described in Section 3 and the original analysis scripts.

---

## 5. De-identification verification

The following checks confirm that no personally identifiable information is
present in the released dataset:

1. **No name fields:** No column contains respondent names or identifiers
2. **No institution names:** Replaced by `tier_code` (1–4) and `elite` (0/1)
3. **No free-text fields:** All categorical responses are coded numerically or
   as fixed-label strings (e.g., `origin` = rural/urban)
4. **No geographic detail below region level:** `region_code` has 7 values
   (major regions of China), not province-level
5. **No dates:** No timestamp or date columns are present
6. **No open-ended responses:** Only Likert-scale and categorical items

The de-identification protocol and dataset are suitable for reviewer access
and comply with the study's ethics approval.

---

## 6. File manifest

| File | Description |
|---|---|
| `06_Data_Deidentified_reviewer.csv` | De-identified dataset (16,075 × 46) |
| `06_Data_codebook.md` | Variable dictionary and reproduction checks |
| `06_Data_README.md` | Overview and upload guidance |
| `06_Data_Reproducibility.md` | This document |
| `_verify_final.py` | Verification script (154 checks, all PASS) |

---

## 7. How to reproduce

1. Install Python 3.13+ with the packages listed in Section 1.1
2. Download `06_Data_Deidentified_reviewer.csv` and `06_Data_codebook.md`
3. Run the code blocks in Section 3 in order
4. To run the full verification, execute:
   ```
   python _verify_final.py
   ```
5. Expected output: "SUMMARY: 154/154 PASS, 0 FAIL"

For questions about the data or analysis code, contact the corresponding author
as stated in the manuscript's Data Availability statement.
