py-geodetector 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ .vscode
2
+ __pycache__
3
+ tempCodeRunnerFile.py
4
+ ref
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.3
2
+ Name: py_geodetector
3
+ Version: 0.1.1
4
+ Summary: A simple Python package for the geodetector
5
+ Project-URL: Homepage, https://github.com/djw-easy/GeoDetector
6
+ Project-URL: Bug Tracker, https://github.com/djw-easy/GeoDetector/issues
7
+ Author-email: djw <djweasy@163.com>
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Python: >=3.7
12
+ Description-Content-Type: text/markdown
13
+
14
+ # A simple Python package for the geodetector
15
+
16
+ # Install
17
+
18
+ ```
19
+ pip install py-geodetector
20
+ ```
21
+
22
+ # Usage
23
+
24
+ A quick example of geodetector usage is given in the ./example.ipynb.
25
+
26
+ ```python
27
+ from py_geodetector import load_example_data, GeoDetector
28
+
29
+ # load example data
30
+ df = load_example_data()
31
+
32
+ gd = GeoDetector(df)
33
+ # factor detect
34
+ factor_df = gd.factor_dector()
35
+
36
+ # interaction detect
37
+ interaction_df = gd.interaction_detector()
38
+ # or you can generate the interaction relationship as the same time
39
+ interaction_df, interaction_relationship_df = gd.interaction_detector(relationship=True)
40
+
41
+ # ecological detect
42
+ ecological_df = gd.ecological_detector()
43
+
44
+ # plot
45
+ # use a heatmap visualize the interaction detect result,
46
+ # red text means that the ecological detection results show a significant difference
47
+ gd.plot(value_fontsize=14, tick_fontsize=16, colorbar_fontsize=14);
48
+ ```
49
+
50
+ # Reference
51
+
52
+ ```
53
+ @article{wang2010geographical,
54
+ title={Geographical detectors-based health risk assessment and its application in the neural tube defects study of the Heshun Region, China},
55
+ author={Wang, Jin-Feng and Li, Xin-Hu and Christakos, George and Liao, Yi-Lan and Zhang, Tin and Gu, Xue and Zheng, Xiao-Ying},
56
+ journal={International Journal of Geographical Information Science},
57
+ volume={24},
58
+ number={1},
59
+ pages={107-127},
60
+ year={2010},
61
+ publisher={Taylor \& Francis}
62
+ }
63
+ ```
@@ -0,0 +1,50 @@
1
+ # A simple Python package for the geodetector
2
+
3
+ # Install
4
+
5
+ ```
6
+ pip install py-geodetector
7
+ ```
8
+
9
+ # Usage
10
+
11
+ A quick example of geodetector usage is given in the ./example.ipynb.
12
+
13
+ ```python
14
+ from py_geodetector import load_example_data, GeoDetector
15
+
16
+ # load example data
17
+ df = load_example_data()
18
+
19
+ gd = GeoDetector(df)
20
+ # factor detect
21
+ factor_df = gd.factor_dector()
22
+
23
+ # interaction detect
24
+ interaction_df = gd.interaction_detector()
25
+ # or you can generate the interaction relationship as the same time
26
+ interaction_df, interaction_relationship_df = gd.interaction_detector(relationship=True)
27
+
28
+ # ecological detect
29
+ ecological_df = gd.ecological_detector()
30
+
31
+ # plot
32
+ # use a heatmap visualize the interaction detect result,
33
+ # red text means that the ecological detection results show a significant difference
34
+ gd.plot(value_fontsize=14, tick_fontsize=16, colorbar_fontsize=14);
35
+ ```
36
+
37
+ # Reference
38
+
39
+ ```
40
+ @article{wang2010geographical,
41
+ title={Geographical detectors-based health risk assessment and its application in the neural tube defects study of the Heshun Region, China},
42
+ author={Wang, Jin-Feng and Li, Xin-Hu and Christakos, George and Liao, Yi-Lan and Zhang, Tin and Gu, Xue and Zheng, Xiao-Ying},
43
+ journal={International Journal of Geographical Information Science},
44
+ volume={24},
45
+ number={1},
46
+ pages={107-127},
47
+ year={2010},
48
+ publisher={Taylor \& Francis}
49
+ }
50
+ ```
@@ -0,0 +1,437 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "metadata": {},
7
+ "outputs": [],
8
+ "source": [
9
+ "from py_geodetector import load_example_data, GeoDetector"
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "code",
14
+ "execution_count": 2,
15
+ "metadata": {},
16
+ "outputs": [
17
+ {
18
+ "data": {
19
+ "text/html": [
20
+ "<div>\n",
21
+ "<style scoped>\n",
22
+ " .dataframe tbody tr th:only-of-type {\n",
23
+ " vertical-align: middle;\n",
24
+ " }\n",
25
+ "\n",
26
+ " .dataframe tbody tr th {\n",
27
+ " vertical-align: top;\n",
28
+ " }\n",
29
+ "\n",
30
+ " .dataframe thead th {\n",
31
+ " text-align: right;\n",
32
+ " }\n",
33
+ "</style>\n",
34
+ "<table border=\"1\" class=\"dataframe\">\n",
35
+ " <thead>\n",
36
+ " <tr style=\"text-align: right;\">\n",
37
+ " <th></th>\n",
38
+ " <th>incidence</th>\n",
39
+ " <th>type</th>\n",
40
+ " <th>region</th>\n",
41
+ " <th>level</th>\n",
42
+ " </tr>\n",
43
+ " </thead>\n",
44
+ " <tbody>\n",
45
+ " <tr>\n",
46
+ " <th>0</th>\n",
47
+ " <td>5.94</td>\n",
48
+ " <td>7</td>\n",
49
+ " <td>5</td>\n",
50
+ " <td>5</td>\n",
51
+ " </tr>\n",
52
+ " <tr>\n",
53
+ " <th>1</th>\n",
54
+ " <td>5.87</td>\n",
55
+ " <td>5</td>\n",
56
+ " <td>5</td>\n",
57
+ " <td>5</td>\n",
58
+ " </tr>\n",
59
+ " <tr>\n",
60
+ " <th>2</th>\n",
61
+ " <td>5.92</td>\n",
62
+ " <td>5</td>\n",
63
+ " <td>5</td>\n",
64
+ " <td>5</td>\n",
65
+ " </tr>\n",
66
+ " <tr>\n",
67
+ " <th>3</th>\n",
68
+ " <td>6.32</td>\n",
69
+ " <td>1</td>\n",
70
+ " <td>7</td>\n",
71
+ " <td>1</td>\n",
72
+ " </tr>\n",
73
+ " <tr>\n",
74
+ " <th>4</th>\n",
75
+ " <td>6.49</td>\n",
76
+ " <td>3</td>\n",
77
+ " <td>2</td>\n",
78
+ " <td>4</td>\n",
79
+ " </tr>\n",
80
+ " </tbody>\n",
81
+ "</table>\n",
82
+ "</div>"
83
+ ],
84
+ "text/plain": [
85
+ " incidence type region level\n",
86
+ "0 5.94 7 5 5\n",
87
+ "1 5.87 5 5 5\n",
88
+ "2 5.92 5 5 5\n",
89
+ "3 6.32 1 7 1\n",
90
+ "4 6.49 3 2 4"
91
+ ]
92
+ },
93
+ "execution_count": 2,
94
+ "metadata": {},
95
+ "output_type": "execute_result"
96
+ }
97
+ ],
98
+ "source": [
99
+ "df = load_example_data()\n",
100
+ "df.head()"
101
+ ]
102
+ },
103
+ {
104
+ "cell_type": "code",
105
+ "execution_count": 3,
106
+ "metadata": {},
107
+ "outputs": [],
108
+ "source": [
109
+ "gd = GeoDetector(df, 'incidence', ['type', 'region', 'level'])"
110
+ ]
111
+ },
112
+ {
113
+ "cell_type": "code",
114
+ "execution_count": 4,
115
+ "metadata": {},
116
+ "outputs": [
117
+ {
118
+ "data": {
119
+ "text/html": [
120
+ "<div>\n",
121
+ "<style scoped>\n",
122
+ " .dataframe tbody tr th:only-of-type {\n",
123
+ " vertical-align: middle;\n",
124
+ " }\n",
125
+ "\n",
126
+ " .dataframe tbody tr th {\n",
127
+ " vertical-align: top;\n",
128
+ " }\n",
129
+ "\n",
130
+ " .dataframe thead th {\n",
131
+ " text-align: right;\n",
132
+ " }\n",
133
+ "</style>\n",
134
+ "<table border=\"1\" class=\"dataframe\">\n",
135
+ " <thead>\n",
136
+ " <tr style=\"text-align: right;\">\n",
137
+ " <th></th>\n",
138
+ " <th>type</th>\n",
139
+ " <th>region</th>\n",
140
+ " <th>level</th>\n",
141
+ " </tr>\n",
142
+ " </thead>\n",
143
+ " <tbody>\n",
144
+ " <tr>\n",
145
+ " <th>q statistic</th>\n",
146
+ " <td>0.385717</td>\n",
147
+ " <td>0.637774</td>\n",
148
+ " <td>0.606709</td>\n",
149
+ " </tr>\n",
150
+ " <tr>\n",
151
+ " <th>p value</th>\n",
152
+ " <td>0.363236</td>\n",
153
+ " <td>0.000117</td>\n",
154
+ " <td>0.040804</td>\n",
155
+ " </tr>\n",
156
+ " </tbody>\n",
157
+ "</table>\n",
158
+ "</div>"
159
+ ],
160
+ "text/plain": [
161
+ " type region level\n",
162
+ "q statistic 0.385717 0.637774 0.606709\n",
163
+ "p value 0.363236 0.000117 0.040804"
164
+ ]
165
+ },
166
+ "execution_count": 4,
167
+ "metadata": {},
168
+ "output_type": "execute_result"
169
+ }
170
+ ],
171
+ "source": [
172
+ "gd.factor_dector()"
173
+ ]
174
+ },
175
+ {
176
+ "cell_type": "code",
177
+ "execution_count": 5,
178
+ "metadata": {},
179
+ "outputs": [
180
+ {
181
+ "data": {
182
+ "text/html": [
183
+ "<div>\n",
184
+ "<style scoped>\n",
185
+ " .dataframe tbody tr th:only-of-type {\n",
186
+ " vertical-align: middle;\n",
187
+ " }\n",
188
+ "\n",
189
+ " .dataframe tbody tr th {\n",
190
+ " vertical-align: top;\n",
191
+ " }\n",
192
+ "\n",
193
+ " .dataframe thead th {\n",
194
+ " text-align: right;\n",
195
+ " }\n",
196
+ "</style>\n",
197
+ "<table border=\"1\" class=\"dataframe\">\n",
198
+ " <thead>\n",
199
+ " <tr style=\"text-align: right;\">\n",
200
+ " <th></th>\n",
201
+ " <th>type</th>\n",
202
+ " <th>region</th>\n",
203
+ " <th>level</th>\n",
204
+ " </tr>\n",
205
+ " </thead>\n",
206
+ " <tbody>\n",
207
+ " <tr>\n",
208
+ " <th>type</th>\n",
209
+ " <td>0.385717</td>\n",
210
+ " <td>NaN</td>\n",
211
+ " <td>NaN</td>\n",
212
+ " </tr>\n",
213
+ " <tr>\n",
214
+ " <th>region</th>\n",
215
+ " <td>0.735681</td>\n",
216
+ " <td>0.637774</td>\n",
217
+ " <td>NaN</td>\n",
218
+ " </tr>\n",
219
+ " <tr>\n",
220
+ " <th>level</th>\n",
221
+ " <td>0.663524</td>\n",
222
+ " <td>0.713597</td>\n",
223
+ " <td>0.606709</td>\n",
224
+ " </tr>\n",
225
+ " </tbody>\n",
226
+ "</table>\n",
227
+ "</div>"
228
+ ],
229
+ "text/plain": [
230
+ " type region level\n",
231
+ "type 0.385717 NaN NaN\n",
232
+ "region 0.735681 0.637774 NaN\n",
233
+ "level 0.663524 0.713597 0.606709"
234
+ ]
235
+ },
236
+ "execution_count": 5,
237
+ "metadata": {},
238
+ "output_type": "execute_result"
239
+ }
240
+ ],
241
+ "source": [
242
+ "gd.interaction_detector()"
243
+ ]
244
+ },
245
+ {
246
+ "cell_type": "code",
247
+ "execution_count": 6,
248
+ "metadata": {},
249
+ "outputs": [
250
+ {
251
+ "data": {
252
+ "text/html": [
253
+ "<div>\n",
254
+ "<style scoped>\n",
255
+ " .dataframe tbody tr th:only-of-type {\n",
256
+ " vertical-align: middle;\n",
257
+ " }\n",
258
+ "\n",
259
+ " .dataframe tbody tr th {\n",
260
+ " vertical-align: top;\n",
261
+ " }\n",
262
+ "\n",
263
+ " .dataframe thead th {\n",
264
+ " text-align: right;\n",
265
+ " }\n",
266
+ "</style>\n",
267
+ "<table border=\"1\" class=\"dataframe\">\n",
268
+ " <thead>\n",
269
+ " <tr style=\"text-align: right;\">\n",
270
+ " <th></th>\n",
271
+ " <th>type</th>\n",
272
+ " <th>region</th>\n",
273
+ " <th>level</th>\n",
274
+ " </tr>\n",
275
+ " </thead>\n",
276
+ " <tbody>\n",
277
+ " <tr>\n",
278
+ " <th>type</th>\n",
279
+ " <td>NaN</td>\n",
280
+ " <td>NaN</td>\n",
281
+ " <td>NaN</td>\n",
282
+ " </tr>\n",
283
+ " <tr>\n",
284
+ " <th>region</th>\n",
285
+ " <td>Enhance, bi-</td>\n",
286
+ " <td>NaN</td>\n",
287
+ " <td>NaN</td>\n",
288
+ " </tr>\n",
289
+ " <tr>\n",
290
+ " <th>level</th>\n",
291
+ " <td>Enhance, bi-</td>\n",
292
+ " <td>Enhance, bi-</td>\n",
293
+ " <td>NaN</td>\n",
294
+ " </tr>\n",
295
+ " </tbody>\n",
296
+ "</table>\n",
297
+ "</div>"
298
+ ],
299
+ "text/plain": [
300
+ " type region level\n",
301
+ "type NaN NaN NaN\n",
302
+ "region Enhance, bi- NaN NaN\n",
303
+ "level Enhance, bi- Enhance, bi- NaN"
304
+ ]
305
+ },
306
+ "execution_count": 6,
307
+ "metadata": {},
308
+ "output_type": "execute_result"
309
+ }
310
+ ],
311
+ "source": [
312
+ "gd.interaction_detector(relationship=True)[1]"
313
+ ]
314
+ },
315
+ {
316
+ "cell_type": "code",
317
+ "execution_count": 7,
318
+ "metadata": {},
319
+ "outputs": [
320
+ {
321
+ "data": {
322
+ "text/html": [
323
+ "<div>\n",
324
+ "<style scoped>\n",
325
+ " .dataframe tbody tr th:only-of-type {\n",
326
+ " vertical-align: middle;\n",
327
+ " }\n",
328
+ "\n",
329
+ " .dataframe tbody tr th {\n",
330
+ " vertical-align: top;\n",
331
+ " }\n",
332
+ "\n",
333
+ " .dataframe thead th {\n",
334
+ " text-align: right;\n",
335
+ " }\n",
336
+ "</style>\n",
337
+ "<table border=\"1\" class=\"dataframe\">\n",
338
+ " <thead>\n",
339
+ " <tr style=\"text-align: right;\">\n",
340
+ " <th></th>\n",
341
+ " <th>type</th>\n",
342
+ " <th>region</th>\n",
343
+ " <th>level</th>\n",
344
+ " </tr>\n",
345
+ " </thead>\n",
346
+ " <tbody>\n",
347
+ " <tr>\n",
348
+ " <th>type</th>\n",
349
+ " <td>NaN</td>\n",
350
+ " <td>NaN</td>\n",
351
+ " <td>NaN</td>\n",
352
+ " </tr>\n",
353
+ " <tr>\n",
354
+ " <th>region</th>\n",
355
+ " <td>Y</td>\n",
356
+ " <td>NaN</td>\n",
357
+ " <td>NaN</td>\n",
358
+ " </tr>\n",
359
+ " <tr>\n",
360
+ " <th>level</th>\n",
361
+ " <td>Y</td>\n",
362
+ " <td>N</td>\n",
363
+ " <td>NaN</td>\n",
364
+ " </tr>\n",
365
+ " </tbody>\n",
366
+ "</table>\n",
367
+ "</div>"
368
+ ],
369
+ "text/plain": [
370
+ " type region level\n",
371
+ "type NaN NaN NaN\n",
372
+ "region Y NaN NaN\n",
373
+ "level Y N NaN"
374
+ ]
375
+ },
376
+ "execution_count": 7,
377
+ "metadata": {},
378
+ "output_type": "execute_result"
379
+ }
380
+ ],
381
+ "source": [
382
+ "gd.ecological_detector()"
383
+ ]
384
+ },
385
+ {
386
+ "cell_type": "code",
387
+ "execution_count": 8,
388
+ "metadata": {},
389
+ "outputs": [
390
+ {
391
+ "data": {
392
+ "image/png": "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",
393
+ "text/plain": [
394
+ "<Figure size 432x288 with 2 Axes>"
395
+ ]
396
+ },
397
+ "metadata": {
398
+ "needs_background": "light"
399
+ },
400
+ "output_type": "display_data"
401
+ }
402
+ ],
403
+ "source": [
404
+ "gd.plot(value_fontsize=14, tick_fontsize=16, colorbar_fontsize=14);"
405
+ ]
406
+ },
407
+ {
408
+ "cell_type": "code",
409
+ "execution_count": null,
410
+ "metadata": {},
411
+ "outputs": [],
412
+ "source": []
413
+ }
414
+ ],
415
+ "metadata": {
416
+ "kernelspec": {
417
+ "display_name": "geo",
418
+ "language": "python",
419
+ "name": "python3"
420
+ },
421
+ "language_info": {
422
+ "codemirror_mode": {
423
+ "name": "ipython",
424
+ "version": 3
425
+ },
426
+ "file_extension": ".py",
427
+ "mimetype": "text/x-python",
428
+ "name": "python",
429
+ "nbconvert_exporter": "python",
430
+ "pygments_lexer": "ipython3",
431
+ "version": "3.10.12"
432
+ },
433
+ "orig_nbformat": 4
434
+ },
435
+ "nbformat": 4,
436
+ "nbformat_minor": 2
437
+ }
@@ -0,0 +1,29 @@
1
+ [build-system]
2
+ requires = [
3
+ "numpy",
4
+ "pandas",
5
+ "scipy",
6
+ "hatchling",
7
+ "matplotlib"
8
+ ]
9
+ build-backend = "hatchling.build"
10
+
11
+
12
+ [project]
13
+ name = "py_geodetector"
14
+ version = "0.1.1"
15
+ authors = [
16
+ { name = "djw", email = "djweasy@163.com" },
17
+ ]
18
+ description = "A simple Python package for the geodetector"
19
+ readme = "README.md"
20
+ requires-python = ">=3.7"
21
+ classifiers = [
22
+ "Programming Language :: Python :: 3",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Operating System :: OS Independent",
25
+ ]
26
+
27
+ [project.urls]
28
+ "Homepage" = "https://github.com/djw-easy/GeoDetector"
29
+ "Bug Tracker" = "https://github.com/djw-easy/GeoDetector/issues"
File without changes
@@ -0,0 +1 @@
1
+ from .geodetector import GeoDetector, load_example_data
@@ -0,0 +1,186 @@
1
+ incidence,type,region,level
2
+ 5.94,7,5,5
3
+ 5.87,5,5,5
4
+ 5.92,5,5,5
5
+ 6.32,1,7,1
6
+ 6.49,3,2,4
7
+ 6.46,3,2,4
8
+ 6.51,3,2,4
9
+ 6.7,3,2,4
10
+ 6.68,3,2,4
11
+ 6.65,3,2,4
12
+ 6.65,3,2,4
13
+ 6.6,3,2,4
14
+ 6.66,3,2,4
15
+ 5.86,5,5,5
16
+ 6.93,3,2,4
17
+ 6.5,3,2,4
18
+ 6.37,3,2,4
19
+ 6.64,3,2,4
20
+ 6.43,7,9,1
21
+ 7.01,2,9,1
22
+ 6.77,2,9,1
23
+ 6.36,7,9,1
24
+ 6.44,7,9,3
25
+ 6.52,7,9,1
26
+ 5.83,5,6,7
27
+ 6.32,7,9,1
28
+ 6.33,7,9,1
29
+ 6.46,7,9,1
30
+ 6.63,7,9,6
31
+ 6.53,3,9,1
32
+ 6.59,7,9,4
33
+ 6.58,2,9,1
34
+ 6.63,3,9,6
35
+ 6.61,2,9,1
36
+ 7.04,3,9,4
37
+ 5.89,5,6,5
38
+ 6.57,2,9,1
39
+ 6.53,3,9,4
40
+ 6.58,3,9,4
41
+ 6.53,3,9,1
42
+ 6.55,3,9,4
43
+ 6.57,2,9,1
44
+ 6.57,1,7,1
45
+ 6.49,2,7,4
46
+ 6.56,2,9,4
47
+ 6.57,1,7,1
48
+ 5.97,5,6,5
49
+ 6.54,2,7,4
50
+ 6.71,3,9,6
51
+ 6.58,3,9,1
52
+ 6.63,7,9,6
53
+ 7.42,3,2,6
54
+ 6.71,3,4,1
55
+ 6.82,3,2,6
56
+ 6.56,3,2,6
57
+ 6.82,3,2,6
58
+ 5.86,5,6,7
59
+ 6.8,3,2,6
60
+ 7.74,3,2,6
61
+ 6.92,3,2,6
62
+ 6.75,3,2,6
63
+ 7.06,3,2,6
64
+ 7.1,3,2,6
65
+ 6.69,3,4,6
66
+ 6.81,3,2,6
67
+ 6.99,3,2,6
68
+ 6.79,3,2,6
69
+ 5.98,5,6,5
70
+ 7.01,3,2,6
71
+ 7.2,3,2,6
72
+ 6.95,3,2,4
73
+ 6.74,3,4,6
74
+ 6.77,3,4,1
75
+ 6.73,3,4,6
76
+ 6.57,7,3,3
77
+ 7.55,7,3,3
78
+ 6.04,1,6,5
79
+ 6.28,7,3,3
80
+ 6.38,7,3,3
81
+ 6.21,7,3,3
82
+ 6.33,7,3,3
83
+ 6.19,7,3,3
84
+ 6.45,7,3,2
85
+ 6.19,7,3,2
86
+ 6.14,7,3,2
87
+ 6.2,7,3,2
88
+ 6.14,3,3,2
89
+ 5.95,1,5,5
90
+ 6.27,7,3,3
91
+ 6.14,3,3,2
92
+ 6.18,7,1,2
93
+ 6.09,7,1,2
94
+ 6.04,7,1,3
95
+ 6.01,7,5,2
96
+ 6.01,7,5,3
97
+ 6.09,7,1,3
98
+ 5.85,5,6,5
99
+ 5.88,7,5,5
100
+ 5.8,5,6,7
101
+ 5.78,5,6,7
102
+ 5.7,5,6,7
103
+ 5.82,5,6,7
104
+ 5.73,5,6,7
105
+ 5.79,5,6,7
106
+ 5.8,5,6,7
107
+ 6.45,1,8,3
108
+ 6.47,1,8,3
109
+ 6.11,3,8,1
110
+ 5.98,7,5,5
111
+ 6.12,1,8,3
112
+ 6.15,1,8,1
113
+ 6.04,3,8,1
114
+ 6.06,3,8,3
115
+ 5.95,3,8,1
116
+ 6.09,3,8,1
117
+ 6.11,3,8,1
118
+ 6.27,1,8,1
119
+ 6.28,1,8,1
120
+ 6.28,1,8,1
121
+ 5.96,1,5,5
122
+ 6.23,1,8,1
123
+ 6.53,1,8,1
124
+ 6.25,1,8,3
125
+ 6.14,3,1,3
126
+ 6.48,3,1,3
127
+ 6.02,1,1,3
128
+ 6.03,1,1,3
129
+ 6.13,3,1,3
130
+ 6.07,3,1,3
131
+ 6.09,3,1,3
132
+ 5.66,5,5,5
133
+ 6.2,3,1,1
134
+ 6.13,1,1,3
135
+ 5.99,1,1,3
136
+ 6.13,1,1,1
137
+ 6.01,7,1,3
138
+ 6.11,1,1,3
139
+ 6.17,1,1,1
140
+ 6.19,3,1,1
141
+ 6.18,1,1,3
142
+ 6.09,1,1,3
143
+ 5.74,5,5,5
144
+ 6.19,7,1,3
145
+ 6.29,1,1,3
146
+ 6.25,1,1,1
147
+ 6.24,1,1,1
148
+ 6.33,3,1,1
149
+ 6.23,3,1,3
150
+ 6.35,1,8,1
151
+ 6.32,1,1,1
152
+ 6.37,1,1,1
153
+ 6.21,1,1,1
154
+ 5.88,5,5,5
155
+ 6.2,1,1,1
156
+ 6.58,1,8,1
157
+ 6.44,1,8,1
158
+ 6.48,1,8,1
159
+ 7.4,2,8,1
160
+ 6.38,1,7,1
161
+ 6.44,1,8,1
162
+ 6.65,1,8,1
163
+ 6.86,1,8,1
164
+ 6.71,1,3,1
165
+ 6.1,5,5,5
166
+ 6.88,7,3,1
167
+ 6.72,7,3,1
168
+ 6.3,7,3,1
169
+ 6.65,1,7,1
170
+ 6.34,1,8,1
171
+ 7.22,1,3,1
172
+ 6.5,1,3,3
173
+ 6.52,1,3,1
174
+ 6.53,7,3,1
175
+ 6.43,7,3,1
176
+ 5.89,5,5,5
177
+ 6.5,7,3,3
178
+ 6.48,7,3,3
179
+ 6.5,7,3,1
180
+ 6.38,1,7,1
181
+ 6.18,3,1,3
182
+ 6.39,1,7,1
183
+ 6.45,1,8,1
184
+ 6.86,2,7,1
185
+ 6.29,2,7,1
186
+ 6.49,1,7,1
@@ -0,0 +1,191 @@
1
+ import warnings
2
+ import numpy as np
3
+ import pandas as pd
4
+ from typing import Sequence
5
+ from scipy.stats import f, ncf
6
+ import matplotlib.pyplot as plt
7
+
8
+
9
+ from pathlib import Path
10
+ def load_example_data():
11
+ file_path = Path(__file__).parent / "example_data" / "disease.csv"
12
+ df = pd.read_csv(file_path)
13
+ return df
14
+
15
+
16
+ def _plot_value(ax, interaction_df, ecological_df, value_fontsize=10):
17
+ length = len(interaction_df.index)
18
+ for i in range(length):
19
+ for j in range(length):
20
+ if not pd.isna(interaction_df.iloc[i, j]):
21
+ num = str(round(interaction_df.iloc[i, j], 2))
22
+ mark = num[-2:] if 3 == len(num) else num[-3:]
23
+ if 'Y'==ecological_df.iloc[i, j]:
24
+ ax.text(j, i, mark, ha="center", va="center", color="r", fontsize=value_fontsize)
25
+ else:
26
+ ax.text(j, i, mark, ha="center", va="center", color="k", fontsize=value_fontsize)
27
+
28
+
29
+ class GeoDetector(object):
30
+ def __init__(self, df: pd.DataFrame, y: str, factors: Sequence[str]):
31
+ self.df = df
32
+ self.y = y
33
+ self.factors = factors
34
+ self._check_data(df, y, factors)
35
+ self.factor_df, self.interaction_df, self.ecological_df = None, None, None
36
+
37
+ def _check_data(self, df, y, factors):
38
+ for factor in factors:
39
+ if not factor in df.columns:
40
+ raise ValueError('Factor [{}] is not in data')
41
+
42
+ for factor in factors:
43
+ # 检查列的数据类型
44
+ if df[factor].dtype not in ['int64', 'int32', 'int16', 'int8',
45
+ 'uint64', 'uint32', 'uint16', 'uint8',
46
+ 'object', 'string']:
47
+ # 如果数据类型不是整型或字符型,发出警告
48
+ warnings.warn(f"Factor '{factor}' is not of type 'int' or 'str'.")
49
+
50
+ if y not in df.columns:
51
+ raise ValueError('Factor [{}] is not in data')
52
+
53
+ for factor in factors:
54
+ if y==factor:
55
+ raise ValueError("Y variable should not in Factor variables. ")
56
+
57
+ has_null = df.isnull().values.any()
58
+ if has_null:
59
+ raise ValueError("data hava some objects with value NULL")
60
+
61
+ @classmethod
62
+ def _cal_ssw(self, df: pd.DataFrame, y, factor, extra_factor=None):
63
+ def cal_ssw(df: pd.DataFrame, y):
64
+ length = df.shape[0]
65
+ if length==1:
66
+ strataVar = 0
67
+ lamda_1st = np.square(df[y].values[0])
68
+ lamda_2nd = df[y].values[0]
69
+ else:
70
+ strataVar = (length-1) * df[y].var(ddof=1)
71
+
72
+ lamda_1st = np.square(df[y].values.mean())
73
+ lamda_2nd = np.sqrt(length) * df[y].values.mean()
74
+ return strataVar, lamda_1st, lamda_2nd
75
+ if extra_factor==None:
76
+ df2 = df[[y, factor]].groupby(factor).apply(cal_ssw, y=y)
77
+ else:
78
+ df2 = df[[y]+list(set([factor, extra_factor]))].groupby([factor, extra_factor]).apply(cal_ssw, y=y)
79
+ df2 = df2.apply(pd.Series)
80
+ df2 = df2.sum()
81
+ strataVarSum, lamda_1st_sum, lamda_2nd_sum = df2.values
82
+ return strataVarSum, lamda_1st_sum, lamda_2nd_sum
83
+
84
+ @classmethod
85
+ def _cal_q(self, df, y, factor, extra_factor=None):
86
+ strataVarSum, lamda_1st_sum, lamda_2nd_sum = self._cal_ssw(df, y, factor, extra_factor)
87
+ TotalVar = (df.shape[0]-1)*df[y].var(ddof=1)
88
+ q = 1 - strataVarSum/TotalVar
89
+ return q, lamda_1st_sum, lamda_2nd_sum
90
+
91
+ def factor_dector(self):
92
+ self.factor_df = pd.DataFrame(index=["q statistic", "p value"], columns=self.factors, dtype="float32")
93
+ N_var = self.df[self.y].var(ddof=1)
94
+ N_popu = self.df.shape[0]
95
+ for factor in self.factors:
96
+ N_stra = self.df[factor].unique().shape[0]
97
+ q, lamda_1st_sum, lamda_2nd_sum = self._cal_q(self.df, self.y, factor)
98
+
99
+ #lamda value
100
+ lamda = (lamda_1st_sum - np.square(lamda_2nd_sum) / N_popu) / N_var
101
+ # F value
102
+ F_value = (N_popu - N_stra)* q / ((N_stra - 1)* (1 - q))
103
+ #p value
104
+ p_value = ncf.sf(F_value, N_stra - 1, N_popu - N_stra, nc=lamda)
105
+
106
+ self.factor_df.loc["q statistic", factor] = q
107
+ self.factor_df.loc["p value", factor] = p_value
108
+ return self.factor_df
109
+
110
+ @classmethod
111
+ def _interaction_relationship(self, df):
112
+ out_df = pd.DataFrame(index=df.index, columns=df.columns)
113
+ length = len(df.index)
114
+ for i in range(length):
115
+ for j in range(i+1, length):
116
+ factor1, factor2 = df.index[i], df.index[j]
117
+ i_q = df.loc[factor2, factor1]
118
+ q1 = df.loc[factor1, factor1]
119
+ q2 = df.loc[factor2, factor2]
120
+
121
+ if (i_q <= q1 and i_q <= q2):
122
+ outputRls = "Weaken, nonlinear"
123
+ if (i_q < max(q1, q2) and i_q > min(q1, q2)):
124
+ outputRls = "Weaken, uni-"
125
+ if (i_q == (q1 + q2)):
126
+ outputRls = "Independent"
127
+ if (i_q > max(q1, q2)):
128
+ outputRls = "Enhance, bi-"
129
+ if (i_q > (q1 + q2)):
130
+ outputRls = "Enhance, nonlinear"
131
+
132
+ out_df.loc[factor2, factor1] = outputRls
133
+ return out_df
134
+
135
+ def interaction_detector(self, relationship=False):
136
+ self.interaction_df = pd.DataFrame(index=self.factors, columns=self.factors, dtype="float32")
137
+ length = len(self.factors)
138
+ for i in range(0, length):
139
+ for j in range(0, i+1):
140
+ q, _, _ = self._cal_q(self.df, self.y, self.factors[i], self.factors[j])
141
+ self.interaction_df.loc[self.factors[i], self.factors[j]] = q
142
+
143
+ if relationship:
144
+ self.interaction_relationship_df = self._interaction_relationship(self.interaction_df)
145
+ return self.interaction_df, self.interaction_relationship_df
146
+ return self.interaction_df
147
+
148
+ def ecological_detector(self):
149
+ self.ecological_df = pd.DataFrame(index=self.factors, columns=self.factors, dtype="float32")
150
+ length = len(self.factors)
151
+ for i in range(1, length):
152
+ ssw1, _, _ = self._cal_ssw(self.df, self.y, self.factors[i])
153
+ dfn = self.df[self.factors[i]].notna().sum()-1
154
+ for j in range(0, i):
155
+ ssw2, _, _ = self._cal_ssw(self.df, self.y, self.factors[j])
156
+ dfd = self.df[self.factors[j]].notna().sum()-1
157
+ fval = (dfn*(dfd-1)*ssw1)/(dfd*(dfn-1)*ssw2)
158
+ if fval<f.ppf(0.05, dfn, dfn):
159
+ self.ecological_df.loc[self.factors[i], self.factors[j]] = 'Y'
160
+ else:
161
+ self.ecological_df.loc[self.factors[i], self.factors[j]] = 'N'
162
+ return self.ecological_df
163
+
164
+ def plot(self, tick_fontsize=10, value_fontsize=10, colorbar_fontsize=10, show=True):
165
+ if isinstance(self.interaction_df, type(None)):
166
+ self.interaction_detector()
167
+ if isinstance(self.ecological_df, type(None)):
168
+ self.ecological_detector()
169
+
170
+ fig, ax = plt.subplots(constrained_layout=True)
171
+
172
+ im = ax.imshow(self.interaction_df.values, cmap="YlGnBu", vmin=0, vmax=1)
173
+ _plot_value(ax, self.interaction_df, self.ecological_df, value_fontsize=value_fontsize)
174
+
175
+ ax.set_xticks(np.arange(len(self.factors)))
176
+ ax.set_yticks(np.arange(len(self.factors)))
177
+ ax.spines['top'].set_visible(False)
178
+ ax.spines['right'].set_visible(False)
179
+
180
+ ax.set_xticklabels(self.factors, fontsize=tick_fontsize)
181
+ ax.set_yticklabels(self.factors, rotation=45, fontsize=tick_fontsize)
182
+ ax.tick_params(axis='y', pad=0.1)
183
+
184
+ colorbar = fig.colorbar(im, ax=ax, shrink=0.9, pad=0.01, aspect=25, extend="both")
185
+ colorbar.ax.tick_params(labelsize=colorbar_fontsize)
186
+
187
+ if show:
188
+ plt.show()
189
+ return ax
190
+ else:
191
+ return ax