Repository: statsmodels/statsmodels

Issue:
qqplot_2samples   for different nobs
(just a thought)

IIRC, qqplot_2samples only supports nobs1 = nobs2

to support unequal nobs, we could use the ecdf evaluated at the joint points.

We can use the trick in scipy.stats.ks_2samp (and in another 2 sample gof tests that I wrote) or use ecdf.

I think this should work.



Discussion:

Resolving patch:
diff --git a/statsmodels/graphics/gofplots.py b/statsmodels/graphics/gofplots.py
--- a/statsmodels/graphics/gofplots.py
+++ b/statsmodels/graphics/gofplots.py
@@ -602,7 +602,8 @@ def qqplot_2samples(data1, data2, xlabel=None, ylabel=None, line=None,
     data1 : {array_like, ProbPlot}
         Data to plot along x axis.
     data2 : {array_like, ProbPlot}
-        Data to plot along y axis.
+        Data to plot along y axis. Does not need to have the same number of
+        observations as data 1.
     xlabel : {None, str}
         User-provided labels for the x-axis. If None (default),
         other values are used.
@@ -657,7 +658,7 @@ def qqplot_2samples(data1, data2, xlabel=None, ylabel=None, line=None,
 
     .. plot:: plots/graphics_gofplots_qqplot_2samples.py
 
-    >>> fig = qqplot_2samples(pp_x, pp_y, xlabel=None, ylabel=None, \
+    >>> fig = qqplot_2samples(pp_x, pp_y, xlabel=None, ylabel=None,
     ...                       line=None, ax=None)
     """
     if not isinstance(data1, ProbPlot):
@@ -665,9 +666,12 @@ def qqplot_2samples(data1, data2, xlabel=None, ylabel=None, line=None,
 
     if not isinstance(data2, ProbPlot):
         data2 = ProbPlot(data2)
-
-    fig = data1.qqplot(xlabel=xlabel, ylabel=ylabel,
-                       line=line, other=data2, ax=ax)
+    if data2.data.shape[0] >= data1.data.shape[0]:
+        fig = data1.qqplot(xlabel=xlabel, ylabel=ylabel,
+                           line=line, other=data2, ax=ax)
+    else:
+        fig = data2.qqplot(xlabel=ylabel, ylabel=xlabel,
+                           line=line, other=data1, ax=ax)
 
     return fig
 


Test patch:
diff --git a/statsmodels/graphics/tests/test_gofplots.py b/statsmodels/graphics/tests/test_gofplots.py
--- a/statsmodels/graphics/tests/test_gofplots.py
+++ b/statsmodels/graphics/tests/test_gofplots.py
@@ -238,3 +238,19 @@ def test_invalid_dist_config(close_figures):
     mod_fit = sm.OLS(data.endog, data.exog).fit()
     with pytest.raises(TypeError, match=r'dist\(0, 1, 4, loc=0, scale=1\)'):
         sm.ProbPlot(mod_fit.resid, stats.t, distargs=(0, 1, 4))
+
+
+@pytest.mark.matplotlib
+def test_qqplot_unequal():
+    rs = np.random.RandomState(0)
+    data1 = rs.standard_normal(100)
+    data2 = rs.standard_normal(200)
+    fig1 = sm.qqplot_2samples(data1, data2)
+    fig2 = sm.qqplot_2samples(data2, data1)
+    x1, y1 = fig1.get_axes()[0].get_children()[0].get_data()
+    x2, y2 = fig2.get_axes()[0].get_children()[0].get_data()
+    np.testing.assert_allclose(x1, x2)
+    np.testing.assert_allclose(y1, y2)
+    numobj1 = len(fig1.get_axes()[0].get_children())
+    numobj2 = len(fig2.get_axes()[0].get_children())
+    assert numobj1 == numobj2
diff --git a/statsmodels/tsa/statespace/tests/test_exact_diffuse_filtering.py b/statsmodels/tsa/statespace/tests/test_exact_diffuse_filtering.py
--- a/statsmodels/tsa/statespace/tests/test_exact_diffuse_filtering.py
+++ b/statsmodels/tsa/statespace/tests/test_exact_diffuse_filtering.py
@@ -1013,7 +1013,7 @@ def test_nondiagonal_obs_cov(reset_randomstate):
     res2 = mod.smooth([])
 
     atol = 0.002 if PLATFORM_WIN else 1e-5
-    rtol = 0.002 if PLATFORM_WIN else 1e-6
+    rtol = 0.002 if PLATFORM_WIN else 1e-4
     # Here we'll just test a few values
     assert_allclose(res1.llf, res2.llf, rtol=rtol, atol=atol)
     assert_allclose(res1.forecasts[0], res2.forecasts[0],