EducationalTestingService / EducationalTestingService/factor_analyzer

random initial values for rotation matrix in GPA rotations

Open
#116 2 comments 2 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
6
Forks
1
PR merge metrics
No merged PRs in 30d

Description

Hi all. First, thanks so much to the devs of `factor_analyzer`. It's a fantastic and much needed package!

**Is your feature request related to a problem? Please describe.**

As has been pointed out, the GPA rotation methods often converge to local minima ([Browne, 2001](https://doi.org/10.1207/S15327906MBR3601_05); [Bernaards & Jennrich, 2005](https://doi.org/10.1177/0013164404272507); [Nguyen & Waller, 2022](https://doi.org/10.1177/0013164404272507)). As such, it's been recommended researchers initialize the GPA from many starting values when searching for a global minimum. I'm wondering if the `Rotator` class could be amended in order to allow initializing the GPA from different starting values.

**Describe the solution you'd like**

One simple solution would be for the `Rotator` class to accept a new argument, `random_state`, which defaults to `None`. When `random_state = None`, the rotation matrix is initialized with `np.eye(n_cols)` as is currently implemented ([L326](https://github.com/EducationalTestingService/factor_analyzer/blob/main/factor_analyzer/rotator.py#L326), [L416](https://github.com/EducationalTestingService/factor_analyzer/blob/main/factor_analyzer/rotator.py#L416), [L493](https://github.com/EducationalTestingService/factor_analyzer/blob/main/factor_analyzer/rotator.py#L493)). When a user specifies a seed, however, the rotation matrix is initialized as a random orthogonal matrix from [`scipy.stats.ortho_group`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.ortho_group.html). That is,:

```python
# initialize the rotation matrix
_, n_cols = loadings.shape
if self.random_state == None:
rotation_matrix = np.eye(n_cols)
else:
rotation_matrix = sp.stats.ortho_group(dim=n_cols, seed=self.random_state).rvs()
```

A solution like the above could be added to the [`Rotator._oblique`](https://github.com/EducationalTestingService/factor_analyzer/blob/main/factor_analyzer/rotator.py#L293), [`Rotator._orthogonal`](https://github.com/EducationalTestingService/factor_analyzer/blob/main/factor_analyzer/rotator.py#L382), [`Rotator._varimax`](https://github.com/EducationalTestingService/factor_analyzer/blob/main/factor_analyzer/rotator.py#L462) methods (and any others that I missed).

Apologies in advance if the above is already possible and I've simply missed how. If the above sounds like it may be useful, I'd be happy to attempt a PR.

All the best,
Sam (@szorowi1)

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.