dsh-eval-guard 0.2.3 → 0.3.1

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.
Files changed (221) hide show
  1. package/README.md +91 -13
  2. package/assets/commit0/instance.json +30 -0
  3. package/assets/commit0/workspace.zip +0 -0
  4. package/assets/featurebench/hidden/test_regression.py +690 -0
  5. package/assets/featurebench/instance.json +16 -0
  6. package/assets/featurebench/workspace.zip +0 -0
  7. package/assets/swe-bench-pro/hidden/test.patch +36 -0
  8. package/assets/swe-bench-pro/instance.json +10 -0
  9. package/assets/swe-bench-pro/workspace.zip +0 -0
  10. package/eval/README.md +18 -0
  11. package/eval/arena/arena/README.md +21 -0
  12. package/eval/arena/arena/bt.py +104 -0
  13. package/eval/arena/arena/clock.py +15 -0
  14. package/eval/arena/arena/core.py +265 -0
  15. package/eval/arena/arena/run.cmd +24 -0
  16. package/eval/arena/arena/run.sh +11 -0
  17. package/eval/arena/arena/schedule.py +79 -0
  18. package/eval/arena/arena/tournament.py +183 -0
  19. package/eval/arena/bomberman/bots/README.md +16 -0
  20. package/eval/arena/bomberman/bots/deepseek-v4.1-flash.cpp +447 -0
  21. package/eval/arena/bomberman/bots/doubao-seed-2.1.cpp +542 -0
  22. package/eval/arena/bomberman/bots/glm-5.3.cpp +406 -0
  23. package/eval/arena/bomberman/bots/gpt-6-astra.cpp +768 -0
  24. package/eval/arena/bomberman/bots/gpt-6-sol.cpp +444 -0
  25. package/eval/arena/bomberman/bots/kimi-k3.cpp +432 -0
  26. package/eval/arena/bomberman/bots/mimo-v2.6-pro.cpp +529 -0
  27. package/eval/arena/bomberman/bots/opus-5.5.cpp +548 -0
  28. package/eval/arena/bomberman/bots/qwen-3.8-max.cpp +836 -0
  29. package/eval/arena/bomberman/tournament/README.md +11 -0
  30. package/eval/arena/bomberman/tournament/bt.py +56 -0
  31. package/eval/arena/bomberman/tournament/evaluate.py +199 -0
  32. package/eval/arena/bomberman/tournament/run.py +79 -0
  33. package/eval/arena/bomberman/tournament/schedule-2to10.json.gz +0 -0
  34. package/eval/arena/bomberman/workspace/MANIFEST.json +13 -0
  35. package/eval/arena/bomberman/workspace/README.md +29 -0
  36. package/eval/arena/bomberman/workspace/RULES.md +100 -0
  37. package/eval/arena/bomberman/workspace/baseline.cpp +240 -0
  38. package/eval/arena/bomberman/workspace/clock.py +25 -0
  39. package/eval/arena/bomberman/workspace/engine/arena.py +198 -0
  40. package/eval/arena/bomberman/workspace/engine/build.py +35 -0
  41. package/eval/arena/bomberman/workspace/engine/engine.py +256 -0
  42. package/eval/arena/bomberman/workspace/engine/verify_replay.py +24 -0
  43. package/eval/arena/bomberman/workspace/run.cmd +28 -0
  44. package/eval/arena/bomberman/workspace/run.sh +16 -0
  45. package/eval/arena/bomberman/workspace/viewer/index.html +42 -0
  46. package/eval/arena/ctf/bots/README.md +18 -0
  47. package/eval/arena/ctf/bots/deepseek-v4.1-flash.cpp +601 -0
  48. package/eval/arena/ctf/bots/doubao-seed-2.1-turbo.cpp +320 -0
  49. package/eval/arena/ctf/bots/fable-5.1.cpp +634 -0
  50. package/eval/arena/ctf/bots/glm-5.3.cpp +689 -0
  51. package/eval/arena/ctf/bots/gpt-6-astra.cpp +657 -0
  52. package/eval/arena/ctf/bots/gpt-6.1-sol.cpp +725 -0
  53. package/eval/arena/ctf/bots/kimi-k3.cpp +667 -0
  54. package/eval/arena/ctf/bots/mimo-v2.6-pro.cpp +533 -0
  55. package/eval/arena/ctf/bots/opus-5.5.cpp +645 -0
  56. package/eval/arena/ctf/bots/qwen-3.8-max.cpp +726 -0
  57. package/eval/arena/ctf/bots/sonnet-5.5.cpp +569 -0
  58. package/eval/arena/ctf/tournament/README.md +15 -0
  59. package/eval/arena/ctf/tournament/formal-2to8.json +31 -0
  60. package/eval/arena/ctf/tournament/formal-9to15.json +31 -0
  61. package/eval/arena/ctf/workspace/README.md +41 -0
  62. package/eval/arena/ctf/workspace/RULES.md +119 -0
  63. package/eval/arena/ctf/workspace/arena.py +186 -0
  64. package/eval/arena/ctf/workspace/baseline.cpp +257 -0
  65. package/eval/arena/ctf/workspace/clock.py +15 -0
  66. package/eval/arena/ctf/workspace/core.py +265 -0
  67. package/eval/arena/ctf/workspace/engine.py +389 -0
  68. package/eval/arena/ctf/workspace/run.cmd +24 -0
  69. package/eval/arena/ctf/workspace/run.sh +11 -0
  70. package/eval/arena/ctf/workspace/viewer.html +71 -0
  71. package/eval/arena/land/bots/README.md +17 -0
  72. package/eval/arena/land/bots/deepseek-4.1f@workbuddy#1/bot.cpp +608 -0
  73. package/eval/arena/land/bots/glm-5.3@workbuddy#1/bot.cpp +521 -0
  74. package/eval/arena/land/bots/gpt-6-astra@codex#1/bot.cpp +373 -0
  75. package/eval/arena/land/bots/gpt-6-sol@codex#1/bot.cpp +341 -0
  76. package/eval/arena/land/bots/kimi-k3@workbuddy#1/bot.cpp +492 -0
  77. package/eval/arena/land/bots/mimo-2.6-pro@zcode#1/bot.cpp +652 -0
  78. package/eval/arena/land/bots/mystery-model@zcode#1/bot.cpp +466 -0
  79. package/eval/arena/land/bots/opus-5.5@claude-code#1/bot.cpp +630 -0
  80. package/eval/arena/land/bots/sonnet-5@claude-code#1/bot.cpp +619 -0
  81. package/eval/arena/land/tournament/make_replay_page.py +17 -0
  82. package/eval/arena/land/tournament/rate.py +290 -0
  83. package/eval/arena/land/tournament/round_robin.py +172 -0
  84. package/eval/arena/land/tournament/sparring.cpp +356 -0
  85. package/eval/arena/land/workspace/RULES.md +114 -0
  86. package/eval/arena/land/workspace/TASK.md +46 -0
  87. package/eval/arena/land/workspace/anchors/.build/greedy_f2e6d2dea5ca +0 -0
  88. package/eval/arena/land/workspace/anchors/greedy.cpp +194 -0
  89. package/eval/arena/land/workspace/anchors/random.cpp +80 -0
  90. package/eval/arena/land/workspace/clock.py +64 -0
  91. package/eval/arena/land/workspace/engine/build.py +33 -0
  92. package/eval/arena/land/workspace/engine/game.py +273 -0
  93. package/eval/arena/land/workspace/engine/referee.py +308 -0
  94. package/eval/arena/land/workspace/starter/.build/bot_be32b45b4699 +0 -0
  95. package/eval/arena/land/workspace/starter/bot.cpp +82 -0
  96. package/eval/arena/land/workspace/viewer/viewer.html +210 -0
  97. package/eval/arena/tank/bots/README.md +24 -0
  98. package/eval/arena/tank/bots/half1/deepseek-v4.1-flash.cpp +367 -0
  99. package/eval/arena/tank/bots/half1/doubao-seed-evolving.cpp +402 -0
  100. package/eval/arena/tank/bots/half1/gemini-4-argon.cpp +1132 -0
  101. package/eval/arena/tank/bots/half1/glm-5.3.cpp +551 -0
  102. package/eval/arena/tank/bots/half1/gpt-6-astra.cpp +233 -0
  103. package/eval/arena/tank/bots/half1/gpt-6.1-sol-ultrafast.cpp +350 -0
  104. package/eval/arena/tank/bots/half1/gpt-6.1-sol.cpp +78 -0
  105. package/eval/arena/tank/bots/half1/grok-4.7.cpp +1099 -0
  106. package/eval/arena/tank/bots/half1/haiku-5.5.cpp +210 -0
  107. package/eval/arena/tank/bots/half1/kimi-k3.cpp +353 -0
  108. package/eval/arena/tank/bots/half1/mimo-v2.6-pro.cpp +336 -0
  109. package/eval/arena/tank/bots/half1/muse-wonder-full.cpp +560 -0
  110. package/eval/arena/tank/bots/half1/opus-5.5.cpp +435 -0
  111. package/eval/arena/tank/bots/half1/qwen-3.8-max.cpp +537 -0
  112. package/eval/arena/tank/bots/half2/deepseek-v4.1-flash.cpp +425 -0
  113. package/eval/arena/tank/bots/half2/doubao-seed-evolving.cpp +308 -0
  114. package/eval/arena/tank/bots/half2/gemini-4-argon.cpp +1390 -0
  115. package/eval/arena/tank/bots/half2/glm-5.3.cpp +651 -0
  116. package/eval/arena/tank/bots/half2/gpt-6-astra.cpp +241 -0
  117. package/eval/arena/tank/bots/half2/gpt-6.1-sol-ultrafast.cpp +356 -0
  118. package/eval/arena/tank/bots/half2/gpt-6.1-sol.cpp +87 -0
  119. package/eval/arena/tank/bots/half2/grok-4.7.cpp +1205 -0
  120. package/eval/arena/tank/bots/half2/haiku-5.5.cpp +210 -0
  121. package/eval/arena/tank/bots/half2/kimi-k3.cpp +487 -0
  122. package/eval/arena/tank/bots/half2/mimo-v2.6-pro.cpp +336 -0
  123. package/eval/arena/tank/bots/half2/muse-wonder-full.cpp +560 -0
  124. package/eval/arena/tank/bots/half2/opus-5.5.cpp +476 -0
  125. package/eval/arena/tank/bots/half2/qwen-3.8-max.cpp +392 -0
  126. package/eval/arena/tank/tournament/README.md +24 -0
  127. package/eval/arena/tank/tournament/bt.py +218 -0
  128. package/eval/arena/tank/tournament/final.json +202 -0
  129. package/eval/arena/tank/tournament/halftime.json +146 -0
  130. package/eval/arena/tank/tournament/progress.json +911 -0
  131. package/eval/arena/tank/tournament/schedule.py +100 -0
  132. package/eval/arena/tank/tournament/sparring.cpp +928 -0
  133. package/eval/arena/tank/tournament/tournament.py +1426 -0
  134. package/eval/arena/tank/workspace/README.md +169 -0
  135. package/eval/arena/tank/workspace/RULES.md +443 -0
  136. package/eval/arena/tank/workspace/arena.py +862 -0
  137. package/eval/arena/tank/workspace/baseline.cpp +259 -0
  138. package/eval/arena/tank/workspace/clock.py +103 -0
  139. package/eval/arena/tank/workspace/core.py +712 -0
  140. package/eval/arena/tank/workspace/engine.py +573 -0
  141. package/eval/arena/tank/workspace/history/.lock +0 -0
  142. package/eval/arena/tank/workspace/history/20261017-193610-08715287f91f-bot.cpp +109 -0
  143. package/eval/arena/tank/workspace/history/20261017-193619-f00b2d5635fa-gpt-6-astra.cpp +241 -0
  144. package/eval/arena/tank/workspace/history/20261017-193643-63319e0a2b1a-gpt-6_1-sol-ultrafast.cpp +356 -0
  145. package/eval/arena/tank/workspace/history/20261017-193647-41c1341d0a8a-gemini-4-argon.cpp +1390 -0
  146. package/eval/arena/tank/workspace/history/20261017-193652-e34c0f45539a-gpt-6_1-sol.cpp +87 -0
  147. package/eval/arena/tank/workspace/history/20261017-193654-c026348ce96e-opus-5_5.cpp +476 -0
  148. package/eval/arena/tank/workspace/history/20261017-193655-4a21366e94d1-muse-wonder-full.cpp +560 -0
  149. package/eval/arena/tank/workspace/history/20261017-193657-dbc4e9969c52-mimo-v2_6-pro.cpp +336 -0
  150. package/eval/arena/tank/workspace/history/20261017-193703-723cd036eb31-grok-4_7.cpp +1205 -0
  151. package/eval/arena/tank/workspace/history/20261017-193707-1ebbb227be68-deepseek-v4_1-flash.cpp +425 -0
  152. package/eval/arena/tank/workspace/history/20261017-193709-aeb0e7202ad5-doubao-seed-evolving.cpp +308 -0
  153. package/eval/arena/tank/workspace/history/20261017-193710-b21ebeb9bc76-kimi-k3.cpp +487 -0
  154. package/eval/arena/tank/workspace/history/20261017-193712-54cd86531905-haiku-5_5.cpp +210 -0
  155. package/eval/arena/tank/workspace/history/20261017-193714-efdb8e35c0bf-qwen-3_8-max.cpp +392 -0
  156. package/eval/arena/tank/workspace/history/20261017-193715-681423c23fdb-glm-5_3.cpp +651 -0
  157. package/eval/arena/tank/workspace/history/20261017-193717-1a8b3c75181d-sparring.cpp +928 -0
  158. package/eval/arena/tank/workspace/history/20261017-193720-1e0803a541e8-baseline.cpp +259 -0
  159. package/eval/arena/tank/workspace/history/log.jsonl +103 -0
  160. package/eval/arena/tank/workspace/judge/cycles.hpp +53 -0
  161. package/eval/arena/tank/workspace/judge/fast.cpp +337 -0
  162. package/eval/arena/tank/workspace/judge/referee.cpp +125 -0
  163. package/eval/arena/tank/workspace/judge/speed.cpp +99 -0
  164. package/eval/arena/tank/workspace/run.cmd +31 -0
  165. package/eval/arena/tank/workspace/run.sh +23 -0
  166. package/eval/arena/tank/workspace/tank.hpp +1762 -0
  167. package/eval/arena/tank/workspace/tank_main.cpp +215 -0
  168. package/eval/arena/tank/workspace/viewer.html +1150 -0
  169. package/eval/arena/warehouse/workspace/SPEC.md +130 -0
  170. package/eval/arena/warehouse/workspace/TASK.md +71 -0
  171. package/eval/arena/warehouse/workspace/baseline/common.h +88 -0
  172. package/eval/arena/warehouse/workspace/baseline/main.cpp +27 -0
  173. package/eval/arena/warehouse/workspace/baseline/old_stuff.cpp +87 -0
  174. package/eval/arena/warehouse/workspace/baseline/sim.cpp +427 -0
  175. package/eval/arena/warehouse/workspace/baseline/util.cpp +220 -0
  176. package/eval/arena/warehouse/workspace/clock.py +42 -0
  177. package/eval/arena/warehouse/workspace/src/common.h +88 -0
  178. package/eval/arena/warehouse/workspace/src/main.cpp +27 -0
  179. package/eval/arena/warehouse/workspace/src/old_stuff.cpp +87 -0
  180. package/eval/arena/warehouse/workspace/src/sim.cpp +427 -0
  181. package/eval/arena/warehouse/workspace/src/util.cpp +220 -0
  182. package/eval/arena/warehouse/workspace/tools/bench.py +119 -0
  183. package/eval/arena/warehouse/workspace/tools/check_input.py +161 -0
  184. package/eval/arena/warehouse/workspace/tools/gen.py +132 -0
  185. package/eval/arena/warehouse/workspace/tools/measure.py +107 -0
  186. package/eval/arena/warehouse/workspace/workloads/public/pub_crowded.in +1420 -0
  187. package/eval/arena/warehouse/workspace/workloads/public/pub_crowded.out +9162 -0
  188. package/eval/arena/warehouse/workspace/workloads/public/pub_dynamic.in +1465 -0
  189. package/eval/arena/warehouse/workspace/workloads/public/pub_dynamic.out +3439 -0
  190. package/eval/arena/warehouse/workspace/workloads/public/pub_flood.in +2757 -0
  191. package/eval/arena/warehouse/workspace/workloads/public/pub_flood.out +3126 -0
  192. package/eval/arena/warehouse/workspace/workloads/public/pub_small.in +731 -0
  193. package/eval/arena/warehouse/workspace/workloads/public/pub_small.out +2578 -0
  194. package/eval/arena/warehouse/workspace/workloads/public/pub_wide.in +528 -0
  195. package/eval/arena/warehouse/workspace/workloads/public/pub_wide.out +2026 -0
  196. package/eval/bomberman.py +20 -0
  197. package/eval/commit0.py +21 -0
  198. package/eval/common.py +61 -0
  199. package/eval/ctf.py +20 -0
  200. package/eval/featurebench.py +25 -0
  201. package/eval/land.py +20 -0
  202. package/eval/legacy/bomberman-prices.json +4 -0
  203. package/eval/legacy/bomberman.py +161 -0
  204. package/eval/legacy/commit0.py +91 -0
  205. package/eval/legacy/ctf-prices.json +4 -0
  206. package/eval/legacy/ctf.py +178 -0
  207. package/eval/legacy/featurebench.py +103 -0
  208. package/eval/legacy/land-prices.json +4 -0
  209. package/eval/legacy/land.py +177 -0
  210. package/eval/legacy/swe_bench_pro.py +116 -0
  211. package/eval/legacy/tank-prices.json +3 -0
  212. package/eval/legacy/tank.py +408 -0
  213. package/eval/legacy/warehouse-prices.json +4 -0
  214. package/eval/legacy/warehouse.py +365 -0
  215. package/eval/swe-bench-pro.py +25 -0
  216. package/eval/tank.py +20 -0
  217. package/eval/warehouse.py +20 -0
  218. package/lib/client/index.js +376 -63
  219. package/lib/client/index.js.map +1 -1
  220. package/lib/index.js +684 -27
  221. package/package.json +2 -1
@@ -0,0 +1,690 @@
1
+ import warnings
2
+
3
+ import numpy as np
4
+ import matplotlib as mpl
5
+ import matplotlib.pyplot as plt
6
+ import pandas as pd
7
+
8
+ import pytest
9
+ import numpy.testing as npt
10
+ import pandas.testing as pdt
11
+
12
+ try:
13
+ import statsmodels.regression.linear_model as smlm
14
+ _no_statsmodels = False
15
+ except ImportError:
16
+ _no_statsmodels = True
17
+
18
+ from seaborn import regression as lm
19
+ from seaborn.palettes import color_palette
20
+
21
+ rs = np.random.RandomState(0)
22
+
23
+
24
+ class TestLinearPlotter:
25
+
26
+ rs = np.random.RandomState(77)
27
+ df = pd.DataFrame(dict(x=rs.normal(size=60),
28
+ d=rs.randint(-2, 3, 60),
29
+ y=rs.gamma(4, size=60),
30
+ s=np.tile(list("abcdefghij"), 6)))
31
+ df["z"] = df.y + rs.randn(60)
32
+ df["y_na"] = df.y.copy()
33
+ df.loc[[10, 20, 30], 'y_na'] = np.nan
34
+
35
+ def test_establish_variables_from_frame(self):
36
+
37
+ p = lm._LinearPlotter()
38
+ p.establish_variables(self.df, x="x", y="y")
39
+ pdt.assert_series_equal(p.x, self.df.x)
40
+ pdt.assert_series_equal(p.y, self.df.y)
41
+ pdt.assert_frame_equal(p.data, self.df)
42
+
43
+ def test_establish_variables_from_series(self):
44
+
45
+ p = lm._LinearPlotter()
46
+ p.establish_variables(None, x=self.df.x, y=self.df.y)
47
+ pdt.assert_series_equal(p.x, self.df.x)
48
+ pdt.assert_series_equal(p.y, self.df.y)
49
+ assert p.data is None
50
+
51
+ def test_establish_variables_from_array(self):
52
+
53
+ p = lm._LinearPlotter()
54
+ p.establish_variables(None,
55
+ x=self.df.x.values,
56
+ y=self.df.y.values)
57
+ npt.assert_array_equal(p.x, self.df.x)
58
+ npt.assert_array_equal(p.y, self.df.y)
59
+ assert p.data is None
60
+
61
+ def test_establish_variables_from_lists(self):
62
+
63
+ p = lm._LinearPlotter()
64
+ p.establish_variables(None,
65
+ x=self.df.x.values.tolist(),
66
+ y=self.df.y.values.tolist())
67
+ npt.assert_array_equal(p.x, self.df.x)
68
+ npt.assert_array_equal(p.y, self.df.y)
69
+ assert p.data is None
70
+
71
+ def test_establish_variables_from_mix(self):
72
+
73
+ p = lm._LinearPlotter()
74
+ p.establish_variables(self.df, x="x", y=self.df.y)
75
+ pdt.assert_series_equal(p.x, self.df.x)
76
+ pdt.assert_series_equal(p.y, self.df.y)
77
+ pdt.assert_frame_equal(p.data, self.df)
78
+
79
+ def test_establish_variables_from_bad(self):
80
+
81
+ p = lm._LinearPlotter()
82
+ with pytest.raises(ValueError):
83
+ p.establish_variables(None, x="x", y=self.df.y)
84
+
85
+ def test_dropna(self):
86
+
87
+ p = lm._LinearPlotter()
88
+ p.establish_variables(self.df, x="x", y_na="y_na")
89
+ pdt.assert_series_equal(p.x, self.df.x)
90
+ pdt.assert_series_equal(p.y_na, self.df.y_na)
91
+
92
+ p.dropna("x", "y_na")
93
+ mask = self.df.y_na.notnull()
94
+ pdt.assert_series_equal(p.x, self.df.x[mask])
95
+ pdt.assert_series_equal(p.y_na, self.df.y_na[mask])
96
+
97
+
98
+ class TestRegressionPlotter:
99
+
100
+ rs = np.random.RandomState(49)
101
+
102
+ grid = np.linspace(-3, 3, 30)
103
+ n_boot = 100
104
+ bins_numeric = 3
105
+ bins_given = [-1, 0, 1]
106
+
107
+ df = pd.DataFrame(dict(x=rs.normal(size=60),
108
+ d=rs.randint(-2, 3, 60),
109
+ y=rs.gamma(4, size=60),
110
+ s=np.tile(list(range(6)), 10)))
111
+ df["z"] = df.y + rs.randn(60)
112
+ df["y_na"] = df.y.copy()
113
+
114
+ bw_err = rs.randn(6)[df.s.values] * 2
115
+ df.y += bw_err
116
+
117
+ p = 1 / (1 + np.exp(-(df.x * 2 + rs.randn(60))))
118
+ df["c"] = [rs.binomial(1, p_i) for p_i in p]
119
+ df.loc[[10, 20, 30], 'y_na'] = np.nan
120
+
121
+ def test_variables_from_frame(self):
122
+
123
+ p = lm._RegressionPlotter("x", "y", data=self.df, units="s")
124
+
125
+ pdt.assert_series_equal(p.x, self.df.x)
126
+ pdt.assert_series_equal(p.y, self.df.y)
127
+ pdt.assert_series_equal(p.units, self.df.s)
128
+ pdt.assert_frame_equal(p.data, self.df)
129
+
130
+ def test_variables_from_series(self):
131
+
132
+ p = lm._RegressionPlotter(self.df.x, self.df.y, units=self.df.s)
133
+
134
+ npt.assert_array_equal(p.x, self.df.x)
135
+ npt.assert_array_equal(p.y, self.df.y)
136
+ npt.assert_array_equal(p.units, self.df.s)
137
+ assert p.data is None
138
+
139
+ def test_variables_from_mix(self):
140
+
141
+ p = lm._RegressionPlotter("x", self.df.y + 1, data=self.df)
142
+
143
+ npt.assert_array_equal(p.x, self.df.x)
144
+ npt.assert_array_equal(p.y, self.df.y + 1)
145
+ pdt.assert_frame_equal(p.data, self.df)
146
+
147
+ def test_variables_must_be_1d(self):
148
+
149
+ array_2d = np.random.randn(20, 2)
150
+ array_1d = np.random.randn(20)
151
+ with pytest.raises(ValueError):
152
+ lm._RegressionPlotter(array_2d, array_1d)
153
+ with pytest.raises(ValueError):
154
+ lm._RegressionPlotter(array_1d, array_2d)
155
+
156
+ def test_dropna(self):
157
+
158
+ p = lm._RegressionPlotter("x", "y_na", data=self.df)
159
+ assert len(p.x) == pd.notnull(self.df.y_na).sum()
160
+
161
+ p = lm._RegressionPlotter("x", "y_na", data=self.df, dropna=False)
162
+ assert len(p.x) == len(self.df.y_na)
163
+
164
+ @pytest.mark.parametrize("x,y",
165
+ [([1.5], [2]),
166
+ (np.array([1.5]), np.array([2])),
167
+ (pd.Series(1.5), pd.Series(2))])
168
+ def test_singleton(self, x, y):
169
+ p = lm._RegressionPlotter(x, y)
170
+ assert not p.fit_reg
171
+
172
+ def test_ci(self):
173
+
174
+ p = lm._RegressionPlotter("x", "y", data=self.df, ci=95)
175
+ assert p.ci == 95
176
+ assert p.x_ci == 95
177
+
178
+ p = lm._RegressionPlotter("x", "y", data=self.df, ci=95, x_ci=68)
179
+ assert p.ci == 95
180
+ assert p.x_ci == 68
181
+
182
+ p = lm._RegressionPlotter("x", "y", data=self.df, ci=95, x_ci="sd")
183
+ assert p.ci == 95
184
+ assert p.x_ci == "sd"
185
+
186
+ @pytest.mark.skipif(_no_statsmodels, reason="no statsmodels")
187
+ def test_fast_regression(self):
188
+
189
+ p = lm._RegressionPlotter("x", "y", data=self.df, n_boot=self.n_boot)
190
+
191
+ # Fit with the "fast" function, which just does linear algebra
192
+ yhat_fast, _ = p.fit_fast(self.grid)
193
+
194
+ # Fit using the statsmodels function with an OLS model
195
+ yhat_smod, _ = p.fit_statsmodels(self.grid, smlm.OLS)
196
+
197
+ # Compare the vector of y_hat values
198
+ npt.assert_array_almost_equal(yhat_fast, yhat_smod)
199
+
200
+ @pytest.mark.skipif(_no_statsmodels, reason="no statsmodels")
201
+ def test_regress_poly(self):
202
+
203
+ p = lm._RegressionPlotter("x", "y", data=self.df, n_boot=self.n_boot)
204
+
205
+ # Fit an first-order polynomial
206
+ yhat_poly, _ = p.fit_poly(self.grid, 1)
207
+
208
+ # Fit using the statsmodels function with an OLS model
209
+ yhat_smod, _ = p.fit_statsmodels(self.grid, smlm.OLS)
210
+
211
+ # Compare the vector of y_hat values
212
+ npt.assert_array_almost_equal(yhat_poly, yhat_smod)
213
+
214
+ @pytest.mark.parametrize("option", ["logistic", "robust", "lowess"])
215
+ @pytest.mark.skipif(not _no_statsmodels, reason="statsmodels installed")
216
+ def test_statsmodels_missing_errors(self, long_df, option):
217
+ with pytest.raises(RuntimeError, match=rf"`{option}=True` requires"):
218
+ lm.regplot(long_df, x="x", y="y", **{option: True})
219
+
220
+ def test_regress_logx(self):
221
+
222
+ x = np.arange(1, 10)
223
+ y = np.arange(1, 10)
224
+ grid = np.linspace(1, 10, 100)
225
+ p = lm._RegressionPlotter(x, y, n_boot=self.n_boot)
226
+
227
+ yhat_lin, _ = p.fit_fast(grid)
228
+ yhat_log, _ = p.fit_logx(grid)
229
+
230
+ assert yhat_lin[0] > yhat_log[0]
231
+ assert yhat_log[20] > yhat_lin[20]
232
+ assert yhat_lin[90] > yhat_log[90]
233
+
234
+ @pytest.mark.skipif(_no_statsmodels, reason="no statsmodels")
235
+ def test_regress_n_boot(self):
236
+
237
+ p = lm._RegressionPlotter("x", "y", data=self.df, n_boot=self.n_boot)
238
+
239
+ # Fast (linear algebra) version
240
+ _, boots_fast = p.fit_fast(self.grid)
241
+ npt.assert_equal(boots_fast.shape, (self.n_boot, self.grid.size))
242
+
243
+ # Slower (np.polyfit) version
244
+ _, boots_poly = p.fit_poly(self.grid, 1)
245
+ npt.assert_equal(boots_poly.shape, (self.n_boot, self.grid.size))
246
+
247
+ # Slowest (statsmodels) version
248
+ _, boots_smod = p.fit_statsmodels(self.grid, smlm.OLS)
249
+ npt.assert_equal(boots_smod.shape, (self.n_boot, self.grid.size))
250
+
251
+ @pytest.mark.skipif(_no_statsmodels, reason="no statsmodels")
252
+ def test_regress_without_bootstrap(self):
253
+
254
+ p = lm._RegressionPlotter("x", "y", data=self.df,
255
+ n_boot=self.n_boot, ci=None)
256
+
257
+ # Fast (linear algebra) version
258
+ _, boots_fast = p.fit_fast(self.grid)
259
+ assert boots_fast is None
260
+
261
+ # Slower (np.polyfit) version
262
+ _, boots_poly = p.fit_poly(self.grid, 1)
263
+ assert boots_poly is None
264
+
265
+ # Slowest (statsmodels) version
266
+ _, boots_smod = p.fit_statsmodels(self.grid, smlm.OLS)
267
+ assert boots_smod is None
268
+
269
+ def test_regress_bootstrap_seed(self):
270
+
271
+ seed = 200
272
+ p1 = lm._RegressionPlotter("x", "y", data=self.df,
273
+ n_boot=self.n_boot, seed=seed)
274
+ p2 = lm._RegressionPlotter("x", "y", data=self.df,
275
+ n_boot=self.n_boot, seed=seed)
276
+
277
+ _, boots1 = p1.fit_fast(self.grid)
278
+ _, boots2 = p2.fit_fast(self.grid)
279
+ npt.assert_array_equal(boots1, boots2)
280
+
281
+ def test_numeric_bins(self):
282
+
283
+ p = lm._RegressionPlotter(self.df.x, self.df.y)
284
+ x_binned, bins = p.bin_predictor(self.bins_numeric)
285
+ npt.assert_equal(len(bins), self.bins_numeric)
286
+ npt.assert_array_equal(np.unique(x_binned), bins)
287
+
288
+ def test_provided_bins(self):
289
+
290
+ p = lm._RegressionPlotter(self.df.x, self.df.y)
291
+ x_binned, bins = p.bin_predictor(self.bins_given)
292
+ npt.assert_array_equal(np.unique(x_binned), self.bins_given)
293
+
294
+ def test_bin_results(self):
295
+
296
+ p = lm._RegressionPlotter(self.df.x, self.df.y)
297
+ x_binned, bins = p.bin_predictor(self.bins_given)
298
+ assert self.df.x[x_binned == 0].min() > self.df.x[x_binned == -1].max()
299
+ assert self.df.x[x_binned == 1].min() > self.df.x[x_binned == 0].max()
300
+
301
+ def test_scatter_data(self):
302
+
303
+ p = lm._RegressionPlotter(self.df.x, self.df.y)
304
+ x, y = p.scatter_data
305
+ npt.assert_array_equal(x, self.df.x)
306
+ npt.assert_array_equal(y, self.df.y)
307
+
308
+ p = lm._RegressionPlotter(self.df.d, self.df.y)
309
+ x, y = p.scatter_data
310
+ npt.assert_array_equal(x, self.df.d)
311
+ npt.assert_array_equal(y, self.df.y)
312
+
313
+ p = lm._RegressionPlotter(self.df.d, self.df.y, x_jitter=.1)
314
+ x, y = p.scatter_data
315
+ assert (x != self.df.d).any()
316
+ npt.assert_array_less(np.abs(self.df.d - x), np.repeat(.1, len(x)))
317
+ npt.assert_array_equal(y, self.df.y)
318
+
319
+ p = lm._RegressionPlotter(self.df.d, self.df.y, y_jitter=.05)
320
+ x, y = p.scatter_data
321
+ npt.assert_array_equal(x, self.df.d)
322
+ npt.assert_array_less(np.abs(self.df.y - y), np.repeat(.1, len(y)))
323
+
324
+ def test_estimate_data(self):
325
+
326
+ p = lm._RegressionPlotter(self.df.d, self.df.y, x_estimator=np.mean)
327
+
328
+ x, y, ci = p.estimate_data
329
+
330
+ npt.assert_array_equal(x, np.sort(np.unique(self.df.d)))
331
+ npt.assert_array_almost_equal(y, self.df.groupby("d").y.mean())
332
+ npt.assert_array_less(np.array(ci)[:, 0], y)
333
+ npt.assert_array_less(y, np.array(ci)[:, 1])
334
+
335
+ def test_estimate_cis(self):
336
+
337
+ seed = 123
338
+
339
+ p = lm._RegressionPlotter(self.df.d, self.df.y,
340
+ x_estimator=np.mean, ci=95, seed=seed)
341
+ _, _, ci_big = p.estimate_data
342
+
343
+ p = lm._RegressionPlotter(self.df.d, self.df.y,
344
+ x_estimator=np.mean, ci=50, seed=seed)
345
+ _, _, ci_wee = p.estimate_data
346
+ npt.assert_array_less(np.diff(ci_wee), np.diff(ci_big))
347
+
348
+ p = lm._RegressionPlotter(self.df.d, self.df.y,
349
+ x_estimator=np.mean, ci=None)
350
+ _, _, ci_nil = p.estimate_data
351
+ npt.assert_array_equal(ci_nil, [None] * len(ci_nil))
352
+
353
+ def test_estimate_units(self):
354
+
355
+ # Seed the RNG locally
356
+ seed = 345
357
+
358
+ p = lm._RegressionPlotter("x", "y", data=self.df,
359
+ units="s", seed=seed, x_bins=3)
360
+ _, _, ci_big = p.estimate_data
361
+ ci_big = np.diff(ci_big, axis=1)
362
+
363
+ p = lm._RegressionPlotter("x", "y", data=self.df, seed=seed, x_bins=3)
364
+ _, _, ci_wee = p.estimate_data
365
+ ci_wee = np.diff(ci_wee, axis=1)
366
+
367
+ npt.assert_array_less(ci_wee, ci_big)
368
+
369
+ def test_partial(self):
370
+
371
+ x = self.rs.randn(100)
372
+ y = x + self.rs.randn(100)
373
+ z = x + self.rs.randn(100)
374
+
375
+ p = lm._RegressionPlotter(y, z)
376
+ _, r_orig = np.corrcoef(p.x, p.y)[0]
377
+
378
+ p = lm._RegressionPlotter(y, z, y_partial=x)
379
+ _, r_semipartial = np.corrcoef(p.x, p.y)[0]
380
+ assert r_semipartial < r_orig
381
+
382
+ p = lm._RegressionPlotter(y, z, x_partial=x, y_partial=x)
383
+ _, r_partial = np.corrcoef(p.x, p.y)[0]
384
+ assert r_partial < r_orig
385
+
386
+ x = pd.Series(x)
387
+ y = pd.Series(y)
388
+ p = lm._RegressionPlotter(y, z, x_partial=x, y_partial=x)
389
+ _, r_partial = np.corrcoef(p.x, p.y)[0]
390
+ assert r_partial < r_orig
391
+
392
+ @pytest.mark.skipif(_no_statsmodels, reason="no statsmodels")
393
+ def test_logistic_regression(self):
394
+
395
+ p = lm._RegressionPlotter("x", "c", data=self.df,
396
+ logistic=True, n_boot=self.n_boot)
397
+ _, yhat, _ = p.fit_regression(x_range=(-3, 3))
398
+ npt.assert_array_less(yhat, 1)
399
+ npt.assert_array_less(0, yhat)
400
+
401
+ @pytest.mark.skipif(_no_statsmodels, reason="no statsmodels")
402
+ def test_logistic_perfect_separation(self):
403
+
404
+ y = self.df.x > self.df.x.mean()
405
+ p = lm._RegressionPlotter("x", y, data=self.df,
406
+ logistic=True, n_boot=10)
407
+ with warnings.catch_warnings():
408
+ warnings.simplefilter("ignore", RuntimeWarning)
409
+ _, yhat, _ = p.fit_regression(x_range=(-3, 3))
410
+ assert np.isnan(yhat).all()
411
+
412
+ @pytest.mark.skipif(_no_statsmodels, reason="no statsmodels")
413
+ def test_robust_regression(self):
414
+
415
+ p_ols = lm._RegressionPlotter("x", "y", data=self.df,
416
+ n_boot=self.n_boot)
417
+ _, ols_yhat, _ = p_ols.fit_regression(x_range=(-3, 3))
418
+
419
+ p_robust = lm._RegressionPlotter("x", "y", data=self.df,
420
+ robust=True, n_boot=self.n_boot)
421
+ _, robust_yhat, _ = p_robust.fit_regression(x_range=(-3, 3))
422
+
423
+ assert len(ols_yhat) == len(robust_yhat)
424
+
425
+ @pytest.mark.skipif(_no_statsmodels, reason="no statsmodels")
426
+ def test_lowess_regression(self):
427
+
428
+ p = lm._RegressionPlotter("x", "y", data=self.df, lowess=True)
429
+ grid, yhat, err_bands = p.fit_regression(x_range=(-3, 3))
430
+
431
+ assert len(grid) == len(yhat)
432
+ assert err_bands is None
433
+
434
+ def test_regression_options(self):
435
+
436
+ with pytest.raises(ValueError):
437
+ lm._RegressionPlotter("x", "y", data=self.df,
438
+ lowess=True, order=2)
439
+
440
+ with pytest.raises(ValueError):
441
+ lm._RegressionPlotter("x", "y", data=self.df,
442
+ lowess=True, logistic=True)
443
+
444
+ def test_regression_limits(self):
445
+
446
+ f, ax = plt.subplots()
447
+ ax.scatter(self.df.x, self.df.y)
448
+ p = lm._RegressionPlotter("x", "y", data=self.df)
449
+ grid, _, _ = p.fit_regression(ax)
450
+ xlim = ax.get_xlim()
451
+ assert grid.min() == xlim[0]
452
+ assert grid.max() == xlim[1]
453
+
454
+ p = lm._RegressionPlotter("x", "y", data=self.df, truncate=True)
455
+ grid, _, _ = p.fit_regression()
456
+ assert grid.min() == self.df.x.min()
457
+ assert grid.max() == self.df.x.max()
458
+
459
+
460
+ class TestRegressionPlots:
461
+
462
+ rs = np.random.RandomState(56)
463
+ df = pd.DataFrame(dict(x=rs.randn(90),
464
+ y=rs.randn(90) + 5,
465
+ z=rs.randint(0, 1, 90),
466
+ g=np.repeat(list("abc"), 30),
467
+ h=np.tile(list("xy"), 45),
468
+ u=np.tile(np.arange(6), 15)))
469
+ bw_err = rs.randn(6)[df.u.values]
470
+ df.y += bw_err
471
+
472
+ def test_regplot_basic(self):
473
+
474
+ f, ax = plt.subplots()
475
+ lm.regplot(x="x", y="y", data=self.df)
476
+ assert len(ax.lines) == 1
477
+ assert len(ax.collections) == 2
478
+
479
+ x, y = ax.collections[0].get_offsets().T
480
+ npt.assert_array_equal(x, self.df.x)
481
+ npt.assert_array_equal(y, self.df.y)
482
+
483
+ def test_regplot_selective(self):
484
+
485
+ f, ax = plt.subplots()
486
+ ax = lm.regplot(x="x", y="y", data=self.df, scatter=False, ax=ax)
487
+ assert len(ax.lines) == 1
488
+ assert len(ax.collections) == 1
489
+ ax.clear()
490
+
491
+ f, ax = plt.subplots()
492
+ ax = lm.regplot(x="x", y="y", data=self.df, fit_reg=False)
493
+ assert len(ax.lines) == 0
494
+ assert len(ax.collections) == 1
495
+ ax.clear()
496
+
497
+ f, ax = plt.subplots()
498
+ ax = lm.regplot(x="x", y="y", data=self.df, ci=None)
499
+ assert len(ax.lines) == 1
500
+ assert len(ax.collections) == 1
501
+ ax.clear()
502
+
503
+ def test_regplot_scatter_kws_alpha(self):
504
+
505
+ f, ax = plt.subplots()
506
+ color = np.array([[0.3, 0.8, 0.5, 0.5]])
507
+ ax = lm.regplot(x="x", y="y", data=self.df,
508
+ scatter_kws={'color': color})
509
+ assert ax.collections[0]._alpha is None
510
+ assert ax.collections[0]._facecolors[0, 3] == 0.5
511
+
512
+ f, ax = plt.subplots()
513
+ color = np.array([[0.3, 0.8, 0.5]])
514
+ ax = lm.regplot(x="x", y="y", data=self.df,
515
+ scatter_kws={'color': color})
516
+ assert ax.collections[0]._alpha == 0.8
517
+
518
+ f, ax = plt.subplots()
519
+ color = np.array([[0.3, 0.8, 0.5]])
520
+ ax = lm.regplot(x="x", y="y", data=self.df,
521
+ scatter_kws={'color': color, 'alpha': 0.4})
522
+ assert ax.collections[0]._alpha == 0.4
523
+
524
+ f, ax = plt.subplots()
525
+ color = 'r'
526
+ ax = lm.regplot(x="x", y="y", data=self.df,
527
+ scatter_kws={'color': color})
528
+ assert ax.collections[0]._alpha == 0.8
529
+
530
+ f, ax = plt.subplots()
531
+ alpha = .3
532
+ ax = lm.regplot(x="x", y="y", data=self.df,
533
+ x_bins=5, fit_reg=False,
534
+ scatter_kws={"alpha": alpha})
535
+ for line in ax.lines:
536
+ assert line.get_alpha() == alpha
537
+
538
+ def test_regplot_binned(self):
539
+
540
+ ax = lm.regplot(x="x", y="y", data=self.df, x_bins=5)
541
+ assert len(ax.lines) == 6
542
+ assert len(ax.collections) == 2
543
+
544
+ def test_lmplot_no_data(self):
545
+
546
+ with pytest.raises(TypeError):
547
+ # keyword argument `data` is required
548
+ lm.lmplot(x="x", y="y")
549
+
550
+ def test_lmplot_basic(self):
551
+
552
+ g = lm.lmplot(x="x", y="y", data=self.df)
553
+ ax = g.axes[0, 0]
554
+ assert len(ax.lines) == 1
555
+ assert len(ax.collections) == 2
556
+
557
+ x, y = ax.collections[0].get_offsets().T
558
+ npt.assert_array_equal(x, self.df.x)
559
+ npt.assert_array_equal(y, self.df.y)
560
+
561
+ def test_lmplot_hue(self):
562
+
563
+ g = lm.lmplot(x="x", y="y", data=self.df, hue="h")
564
+ ax = g.axes[0, 0]
565
+
566
+ assert len(ax.lines) == 2
567
+ assert len(ax.collections) == 4
568
+
569
+ def test_lmplot_markers(self):
570
+
571
+ g1 = lm.lmplot(x="x", y="y", data=self.df, hue="h", markers="s")
572
+ assert g1.hue_kws == {"marker": ["s", "s"]}
573
+
574
+ g2 = lm.lmplot(x="x", y="y", data=self.df, hue="h", markers=["o", "s"])
575
+ assert g2.hue_kws == {"marker": ["o", "s"]}
576
+
577
+ with pytest.raises(ValueError):
578
+ lm.lmplot(x="x", y="y", data=self.df, hue="h",
579
+ markers=["o", "s", "d"])
580
+
581
+ def test_lmplot_marker_linewidths(self):
582
+
583
+ g = lm.lmplot(x="x", y="y", data=self.df, hue="h",
584
+ fit_reg=False, markers=["o", "+"])
585
+ c = g.axes[0, 0].collections
586
+ assert c[1].get_linewidths()[0] == mpl.rcParams["lines.linewidth"]
587
+
588
+ def test_lmplot_facets(self):
589
+
590
+ g = lm.lmplot(x="x", y="y", data=self.df, row="g", col="h")
591
+ assert g.axes.shape == (3, 2)
592
+
593
+ g = lm.lmplot(x="x", y="y", data=self.df, col="u", col_wrap=4)
594
+ assert g.axes.shape == (6,)
595
+
596
+ g = lm.lmplot(x="x", y="y", data=self.df, hue="h", col="u")
597
+ assert g.axes.shape == (1, 6)
598
+
599
+ def test_lmplot_hue_col_nolegend(self):
600
+
601
+ g = lm.lmplot(x="x", y="y", data=self.df, col="h", hue="h")
602
+ assert g._legend is None
603
+
604
+ def test_lmplot_scatter_kws(self):
605
+
606
+ g = lm.lmplot(x="x", y="y", hue="h", data=self.df, ci=None)
607
+ red_scatter, blue_scatter = g.axes[0, 0].collections
608
+
609
+ red, blue = color_palette(n_colors=2)
610
+ npt.assert_array_equal(red, red_scatter.get_facecolors()[0, :3])
611
+ npt.assert_array_equal(blue, blue_scatter.get_facecolors()[0, :3])
612
+
613
+ @pytest.mark.parametrize("sharex", [True, False])
614
+ def test_lmplot_facet_truncate(self, sharex):
615
+
616
+ g = lm.lmplot(
617
+ data=self.df, x="x", y="y", hue="g", col="h",
618
+ truncate=False, facet_kws=dict(sharex=sharex),
619
+ )
620
+
621
+ for ax in g.axes.flat:
622
+ for line in ax.lines:
623
+ xdata = line.get_xdata()
624
+ assert ax.get_xlim() == tuple(xdata[[0, -1]])
625
+
626
+ def test_lmplot_sharey(self):
627
+
628
+ df = pd.DataFrame(dict(
629
+ x=[0, 1, 2, 0, 1, 2],
630
+ y=[1, -1, 0, -100, 200, 0],
631
+ z=["a", "a", "a", "b", "b", "b"],
632
+ ))
633
+
634
+ with pytest.warns(UserWarning):
635
+ g = lm.lmplot(data=df, x="x", y="y", col="z", sharey=False)
636
+ ax1, ax2 = g.axes.flat
637
+ assert ax1.get_ylim()[0] > ax2.get_ylim()[0]
638
+ assert ax1.get_ylim()[1] < ax2.get_ylim()[1]
639
+
640
+ def test_lmplot_facet_kws(self):
641
+
642
+ xlim = -4, 20
643
+ g = lm.lmplot(
644
+ data=self.df, x="x", y="y", col="h", facet_kws={"xlim": xlim}
645
+ )
646
+ for ax in g.axes.flat:
647
+ assert ax.get_xlim() == xlim
648
+
649
+ def test_residplot(self):
650
+
651
+ x, y = self.df.x, self.df.y
652
+ ax = lm.residplot(x=x, y=y)
653
+
654
+ resid = y - np.polyval(np.polyfit(x, y, 1), x)
655
+ x_plot, y_plot = ax.collections[0].get_offsets().T
656
+
657
+ npt.assert_array_equal(x, x_plot)
658
+ npt.assert_array_almost_equal(resid, y_plot)
659
+
660
+ @pytest.mark.skipif(_no_statsmodels, reason="no statsmodels")
661
+ def test_residplot_lowess(self):
662
+
663
+ ax = lm.residplot(x="x", y="y", data=self.df, lowess=True)
664
+ assert len(ax.lines) == 2
665
+
666
+ x, y = ax.lines[1].get_xydata().T
667
+ npt.assert_array_equal(x, np.sort(self.df.x))
668
+
669
+ @pytest.mark.parametrize("option", ["robust", "lowess"])
670
+ @pytest.mark.skipif(not _no_statsmodels, reason="statsmodels installed")
671
+ def test_residplot_statsmodels_missing_errors(self, long_df, option):
672
+ with pytest.raises(RuntimeError, match=rf"`{option}=True` requires"):
673
+ lm.residplot(long_df, x="x", y="y", **{option: True})
674
+
675
+ def test_three_point_colors(self):
676
+
677
+ x, y = np.random.randn(2, 3)
678
+ ax = lm.regplot(x=x, y=y, color=(1, 0, 0))
679
+ color = ax.collections[0].get_facecolors()
680
+ npt.assert_almost_equal(color[0, :3],
681
+ (1, 0, 0))
682
+
683
+ def test_regplot_xlim(self):
684
+
685
+ f, ax = plt.subplots()
686
+ x, y1, y2 = np.random.randn(3, 50)
687
+ lm.regplot(x=x, y=y1, truncate=False)
688
+ lm.regplot(x=x, y=y2, truncate=False)
689
+ line1, line2 = ax.lines
690
+ assert np.array_equal(line1.get_xdata(), line2.get_xdata())