Skip to content

局部莫兰指数

支持多时期局部 Moran’s I、显著空间集聚象限汇总与按需明细。

核心代码

py
    def _fit_local_moran(self, *, decimals: int, title: str) -> None:
        prepared = self._prepare_panel(require_x=False)
        raw_permutations = self.spatial_options.get('moran_permutations')
        permutations = 999 if raw_permutations is None else max(0, min(int(raw_permutations), 999))
        seed = int(self.spatial_options.get('moran_seed') or 5282026)
        rng = np.random.default_rng(seed)
        selected_times = self._resolve_local_moran_times(
            self.spatial_options.get('moran_local_times'),
            prepared['times'],
        )
        show_details = bool(self.spatial_options.get('moran_show_details', False))
        local_rows: list[dict[str, Any]] = []
        summary_rows: list[dict[str, Any]] = []

        for time_value in selected_times:
            time_index = prepared['times'].index(time_value)
            values = prepared['y'][time_index]
            if np.nanstd(values) <= 1e-12:
                summary_rows.append({
                    'time': time_value,
                    'entities': len(values),
                    'significant': 0,
                    'hh': 0,
                    'll': 0,
                    'hl': 0,
                    'lh': 0,
                    'note': _('该时期变量没有横截面变异,已跳过'),
                })
                continue

            period_rows = self._local_moran(
                values,
                self.weight_matrix,
                prepared['entities'],
                permutations,
                rng,
            )
            for row in period_rows:
                row['time'] = time_value
                row['significant'] = bool(np.isfinite(row['p']) and row['p'] < 0.05)
            significant_rows = [row for row in period_rows if row['significant']]
            summary_rows.append({
                'time': time_value,
                'entities': len(period_rows),
                'significant': len(significant_rows),
                'hh': sum(row['quadrant'] == 'HH' for row in significant_rows),
                'll': sum(row['quadrant'] == 'LL' for row in significant_rows),
                'hl': sum(row['quadrant'] == 'HL' for row in significant_rows),
                'lh': sum(row['quadrant'] == 'LH' for row in significant_rows),
                'note': '',
            })
            local_rows.extend(period_rows)
            scatter = self._moran_scatter_image(values, time_value)
            if scatter:
                self.chart_images.append(scatter)

        self.model_stats = {
            'N': int(prepared['n'] * len(selected_times)),
            'Groups': int(prepared['n']),
            'Periods': int(len(selected_times)),
        }
        self.result_payload = {
            'local_moran_summary': summary_rows,
            'local_moran': local_rows,
            'selected_times': selected_times,
            'show_details': show_details,
            'alignment_report': self.alignment_report,
            'seed': seed,
            'permutations': permutations,
        }
        self.custom_html = self._render_local_moran_html(
            summary_rows,
            local_rows,
            show_details,
            decimals,
            title,
        )
        self.raw_output = json_like_text(self.result_payload)

Released under the AGPL-3.0 License.