# Comparison with other R packages

# Data setup

## Univariate mean change

    # Univariate mean change
    set.seed(1)
    p <- 1
    mean_data_1 <- rbind(
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(100, p)),
      mvtnorm::rmvnorm(400, mean = rep(50, p), sigma = diag(100, p)),
      mvtnorm::rmvnorm(300, mean = rep(2, p), sigma = diag(100, p))
    )

    plot.ts(mean_data_1)

## Univariate mean and/or variance change

    # Univariate mean and/or variance change
    set.seed(1)
    p <- 1
    mv_data_1 <- rbind(
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(400, mean = rep(10, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(100, p)),
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(400, mean = rep(10, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(300, mean = rep(10, p), sigma = diag(100, p))
    )

    plot.ts(mv_data_1)

## Multivariate mean change

    # Multivariate mean change
    set.seed(1)
    p <- 3
    mean_data_3 <- rbind(
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(100, p)),
      mvtnorm::rmvnorm(400, mean = rep(50, p), sigma = diag(100, p)),
      mvtnorm::rmvnorm(300, mean = rep(2, p), sigma = diag(100, p))
    )

    plot.ts(mean_data_3)

## Multivariate mean and/or variance change

    # Multivariate mean and/or variance change
    set.seed(1)
    p <- 4
    mv_data_3 <- rbind(
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(400, mean = rep(10, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(100, p)),
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(400, mean = rep(10, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(300, mean = rep(10, p), sigma = diag(100, p))
    )

    plot.ts(mv_data_3)

## Linear regression

    # Linear regression
    set.seed(1)
    n <- 300
    p <- 4
    x <- mvtnorm::rmvnorm(n, rep(0, p), diag(p))
    theta_0 <- rbind(c(1, 3.2, -1, 0), c(-1, -0.5, 2.5, -2), c(0.8, 0, 1, 2))
    y <- c(
      x[1:100, ] %*% theta_0[1, ] + rnorm(100, 0, 3),
      x[101:200, ] %*% theta_0[2, ] + rnorm(100, 0, 3),
      x[201:n, ] %*% theta_0[3, ] + rnorm(100, 0, 3)
    )
    lm_data <- data.frame(y = y, x = x)

    plot.ts(lm_data)

## Logistic regression

    # Logistic regression
    set.seed(1)
    n <- 500
    p <- 4
    x <- mvtnorm::rmvnorm(n, rep(0, p), diag(p))
    theta <- rbind(rnorm(p, 0, 1), rnorm(p, 2, 1))
    y <- c(
      rbinom(300, 1, 1 / (1 + exp(-x[1:300, ] %*% theta[1, ]))),
      rbinom(200, 1, 1 / (1 + exp(-x[301:n, ] %*% theta[2, ])))
    )
    binomial_data <- data.frame(y = y, x = x)

    plot.ts(binomial_data)

## Poisson regression

    # Poisson regression
    set.seed(1)
    n <- 1100
    p <- 3
    x <- mvtnorm::rmvnorm(n, rep(0, p), diag(p))
    delta <- rnorm(p)
    theta_0 <- c(1, 0.3, -1)
    y <- c(
      rpois(500, exp(x[1:500, ] %*% theta_0)),
      rpois(300, exp(x[501:800, ] %*% (theta_0 + delta))),
      rpois(200, exp(x[801:1000, ] %*% theta_0)),
      rpois(100, exp(x[1001:1100, ] %*% (theta_0 - delta)))
    )
    poisson_data <- data.frame(y = y, x = x)

    plot.ts(log(poisson_data$y))

    plot.ts(poisson_data[, -1])

## Lasso

    # Lasso
    set.seed(1)
    n <- 480
    p_true <- 6
    p <- 50
    x <- mvtnorm::rmvnorm(n, rep(0, p), diag(p))
    theta_0 <- rbind(
      runif(p_true, -5, -2),
      runif(p_true, -3, 3),
      runif(p_true, 2, 5),
      runif(p_true, -5, 5)
    )
    theta_0 <- cbind(theta_0, matrix(0, ncol = p - p_true, nrow = 4))
    y <- c(
      x[1:80, ] %*% theta_0[1, ] + rnorm(80, 0, 1),
      x[81:200, ] %*% theta_0[2, ] + rnorm(120, 0, 1),
      x[201:320, ] %*% theta_0[3, ] + rnorm(120, 0, 1),
      x[321:n, ] %*% theta_0[4, ] + rnorm(160, 0, 1)
    )
    lasso_data <- data.frame(y = y, x = x)

    plot.ts(lasso_data[, seq_len(p_true + 1)])

## AR(3)

    # AR(3)
    set.seed(1)
    n <- 1000
    x <- rep(0, n + 3)
    for (i in 1:600) {
      x[i + 3] <- 0.6 * x[i + 2] - 0.2 * x[i + 1] + 0.1 * x[i] + rnorm(1, 0, 3)
    }
    for (i in 601:1000) {
      x[i + 3] <- 0.3 * x[i + 2] + 0.4 * x[i + 1] + 0.2 * x[i] + rnorm(1, 0, 3)
    }
    ar_data <- x[-seq_len(3)]

    plot.ts(ar_data)

## GARCH(1, 1)

    # GARCH(1, 1)
    set.seed(1)
    n <- 400
    sigma_2 <- rep(1, n + 1)
    x <- rep(0, n + 1)
    for (i in seq_len(200)) {
      sigma_2[i + 1] <- 20 + 0.5 * x[i]^2 + 0.1 * sigma_2[i]
      x[i + 1] <- rnorm(1, 0, sqrt(sigma_2[i + 1]))
    }
    for (i in 201:400) {
      sigma_2[i + 1] <- 1 + 0.1 * x[i]^2 + 0.5 * sigma_2[i]
      x[i + 1] <- rnorm(1, 0, sqrt(sigma_2[i + 1]))
    }
    garch_data <- x[-1]

    plot.ts(garch_data)

## VAR(2)

    # VAR(2)
    set.seed(1)
    n <- 800
    p <- 2
    theta_1 <- matrix(c(-0.3, 0.6, -0.5, 0.4, 0.2, 0.2, 0.2, -0.2), nrow = p)
    theta_2 <- matrix(c(0.3, -0.4, 0.1, -0.5, -0.5, -0.2, -0.5, 0.2), nrow = p)
    x <- matrix(0, n + 2, p)
    for (i in 1:500) {
      x[i + 2, ] <- theta_1 %*% c(x[i + 1, ], x[i, ]) + rnorm(p, 0, 1)
    }
    for (i in 501:n) {
      x[i + 2, ] <- theta_2 %*% c(x[i + 1, ], x[i, ]) + rnorm(p, 0, 1)
    }
    var_data <- x[-seq_len(2), ]

    plot.ts(var_data)

# Univariate mean change

The true change points are 300 and 700. Some methods are plotted due to the un-retrievable change points.

    (fastcpd_result <- fastcpd::fastcpd.mean(mean_data_1, r.progress = FALSE)@cp_set)
    #> error: fastcpd_class_public.cc:85: Invalid family.
    #> error: fastcpd_class_public.cc:99: Invalid family.
    #> [1] 300 700

    (CptNonPar_result <- CptNonPar::np.mojo(mean_data_1, G = floor(length(mean_data_1) / 6))$cpts)
    #> [1] 300 700

    strucchange::breakpoints(y ~ 1, data = data.frame(y = mean_data_1))$breakpoints
    #> [1] 300 700

    ecp::e.divisive(mean_data_1)$estimates
    #> [1]    1  301  701 1001

    (changepoint_result <- changepoint::cpt.mean(c(mean_data_1))@cpts)
    #> [1]  300 1000

    (breakfast_result <- breakfast::breakfast(mean_data_1)$cptmodel.list[[6]]$cpts)
    #> [1] 300 700

    (wbs_result <- wbs::wbs(mean_data_1)$cpt$cpt.ic$mbic.penalty)
    #> [1] 300 700

    mosum::mosum(c(mean_data_1), G = 40)$cpts.info$cpts
    #> [1] 300 700

    (fpop_result <- fpop::Fpop(mean_data_1, nrow(mean_data_1))$t.est)
    #> [1]  300  700 1000

    gfpop::gfpop(
      data = mean_data_1,
      mygraph = gfpop::graph(
        penalty = 2 * log(nrow(mean_data_1)) * gfpop::sdDiff(mean_data_1) ^ 2,
        type = "updown"
      ),
      type = "mean"
    )$changepoints
    #> [1]  300  700 1000

    invisible(
      suppressMessages(
        capture.output(
          result_InspectChangepoint <- InspectChangepoint::inspect(
            t(mean_data_1),
            threshold = InspectChangepoint::compute.threshold(
              nrow(mean_data_1), ncol(mean_data_1)
            )
          )
        )
      )
    )
    result_InspectChangepoint$changepoints[, "location"]
    #> [1] 300 700

    (jointseg_result <- jointseg::jointSeg(mean_data_1, K = 2)$bestBkp)
    #> [1] 300 700

    Rbeast::beast(
      mean_data_1, season = "none", print.progress = FALSE, quiet = TRUE
    )$trend$cp
    #>  [1] 701 301 NaN NaN NaN NaN NaN NaN NaN NaN

    (stepR_result <- stepR::stepFit(mean_data_1, alpha = 0.5)$rightEnd)
    #> [1]  300  700 1000

    (cpm_result <- cpm::processStream(mean_data_1, cpmType = "Student")$changePoints)
    #> [1] 299 699

    (segmented_result <- segmented::stepmented(
      as.numeric(mean_data_1), npsi = 2
    )$psi[, "Est."])
    #> psi1.index psi2.index
    #>   298.1981   699.1524

    plot(
      mcp::mcp(
        list(y ~ 1, ~ 1, ~ 1),
        data = data.frame(y = mean_data_1, x = seq_len(nrow(mean_data_1))),
        par_x = "x"
      )
    )

`mcp` plot for univariate mean change

    plot(not::not(mean_data_1, contrast = "pcwsConstMean"))

    plot(bcp::bcp(mean_data_1))

`bcp` plot for univariate mean change

# Univariate mean and/or variance change

The true change points are 300, 700, 1000, 1300 and 1700. Some methods are plotted due to the un-retrievable change points.

    (fastcpd_result <- fastcpd::fastcpd.mv(mv_data_1, r.progress = FALSE)@cp_set)
    #> error: fastcpd_class_public.cc:85: Invalid family.
    #> error: fastcpd_class_public.cc:99: Invalid family.
    #> [1]  300  700 1001 1300 1700

    ecp::e.divisive(mv_data_1)$estimates
    #> [1]    1  301  701 1001 1301 1701 2001

    (changepoint_result <- changepoint::cpt.meanvar(c(mv_data_1))@cpts)
    #> [1]  300 2000

    (CptNonPar_result <- CptNonPar::np.mojo(mv_data_1, G = floor(length(mv_data_1) / 6))$cpts)
    #> [1]  333  700 1300

    (cpm_result <- cpm::processStream(mv_data_1, cpmType = "GLR")$changePoints)
    #>  [1]  293  300  403  408  618  621  696 1000 1021 1024 1293 1300 1417 1693 1700
    #> [16] 1981

    invisible(
      suppressMessages(
        capture.output(
          result_InspectChangepoint <- InspectChangepoint::inspect(
            t(mv_data_1),
            threshold = InspectChangepoint::compute.threshold(
              nrow(mv_data_1), ncol(mv_data_1)
            )
          )
        )
      )
    )
    result_InspectChangepoint$changepoints[, "location"]
    #>   [1]  300  700  701  702  704  707  708  712  715  716  717  718  721  722
    #>  [15]  723  726  727  729  731  732  734  736  740  742  744  746  748  750
    #>  [29]  753  755  756  757  759  760  762  764  765  768  769  771  772  774
    #>  [43]  776  777  784  785  786  789  791  792  794  797  798  799  801  802
    #>  [57]  803  807  809  810  813  815  817  819  826  827  828  829  831  833
    #>  [71]  835  836  837  838  840  841  842  843  845  848  849  852  854  860
    #>  [85]  862  864  866  868  870  872  875  879  881  884  886  887  888  889
    #>  [99]  896  897  898  899  901  903  904  905  906  909  912  913  915  917
    #> [113]  919  921  922  923  927  928  932  934  936  937  940  944  945  947
    #> [127]  948  949  951  956  958  959  961  962  963  964  966  967  968  972
    #> [141]  974  976  978  979  986  988  990  992  995 1000 1300 1700 1702 1703
    #> [155] 1704 1705 1708 1710 1712 1714 1716 1717 1718 1720 1721 1723 1725 1726
    #> [169] 1727 1729 1731 1733 1735 1736 1737 1739 1742 1745 1747 1748 1752 1754
    #> [183] 1756 1758 1759 1760 1766 1768 1770 1771 1773 1775 1778 1782 1784 1785
    #> [197] 1790 1792 1793 1795 1796 1797 1799 1800 1802 1803 1804 1805 1806 1807
    #> [211] 1808 1809 1821 1824 1825 1827 1828 1829 1833 1835 1837 1840 1841 1842
    #> [225] 1848 1849 1851 1852 1854 1855 1857 1859 1860 1862 1863 1865 1867 1868
    #> [239] 1876 1878 1879 1880 1882 1883 1884 1886 1887 1889 1894 1898 1899 1905
    #> [253] 1906 1907 1908 1909 1912 1919 1926 1927 1928 1930 1933 1934 1935 1936
    #> [267] 1938 1940 1944 1947 1950 1952 1954 1955 1956 1960 1962 1963 1965 1966
    #> [281] 1967 1969 1970 1974 1976 1977 1978 1980 1985 1987 1988 1990 1997 1998

    Rbeast::beast(
      mv_data_1, season = "none", print.progress = FALSE, quiet = TRUE
    )$trend$cp
    #>  [1] 1794 1855 1986 1301  301  703 1981 1769 1860 1834

    plot(
      mcp::mcp(
        list(y ~ 1, ~ 1, ~ 1, ~ 1, ~ 1, ~ 1),
        data = data.frame(y = mv_data_1, x = seq_len(nrow(mv_data_1))),
        par_x = "x"
      )
    )

`mcp` plot for univariate mean and/or variance change

    plot(not::not(mv_data_1, contrast = "pcwsConstMeanVar"))

    #> Press [enter] to continue

# Multivariate mean change

The true change points are 300 and 700. Some methods are plotted due to the un-retrievable change points.

    (fastcpd_result <- fastcpd::fastcpd.mean(mean_data_3, r.progress = FALSE)@cp_set)
    #> error: fastcpd_class_public.cc:85: Invalid family.
    #> error: fastcpd_class_public.cc:99: Invalid family.
    #> [1] 300 700

    (CptNonPar_result <- CptNonPar::np.mojo(mean_data_3, G = floor(nrow(mean_data_3) / 6))$cpts)
    #> [1] 300 700

    invisible(
      suppressMessages(
        capture.output(
          result_InspectChangepoint <- InspectChangepoint::inspect(
            t(mean_data_3),
            threshold = InspectChangepoint::compute.threshold(
              nrow(mean_data_3), ncol(mean_data_3)
            )
          )
        )
      )
    )
    result_InspectChangepoint$changepoints[, "location"]
    #> [1] 300 700

    (jointseg_result <- jointseg::jointSeg(mean_data_3, K = 2)$bestBkp)
    #> [1] 300 700

    invisible(
      capture.output(
        result_Rbeast <- Rbeast::beast123(
          mean_data_3,
          metadata = list(whichDimIsTime = 1),
          season = "none"
        )
      )
    )
    result_Rbeast$trend$cp
    #> Warning message:
    #> In Rbeast::beast123(mean_data_3, metadata = list(whichDimIsTime = 1),  :
    #>   WARNING: If the input data is regular and ordered in time,the times of individual datapoints are determined fully by 'metadata$startTime' and 'metadata$deltaTime'. But startTime and deltaTime are missing and a default value 1 is used for both!
    #>       [,1] [,2] [,3]
    #>  [1,]  301  301  701
    #>  [2,]  701  701  301
    #>  [3,]  NaN  225  NaN
    #>  [4,]  NaN  684  NaN
    #>  [5,]  NaN  NaN  NaN
    #>  [6,]  NaN  NaN  NaN
    #>  [7,]  NaN  NaN  NaN
    #>  [8,]  NaN  NaN  NaN
    #>  [9,]  NaN  NaN  NaN
    #> [10,]  NaN  NaN  NaN

    strucchange::breakpoints(
      cbind(y.1, y.2, y.3) ~ 1, data = data.frame(y = mean_data_3)
    )$breakpoints
    #> [1] 300 700

    ecp::e.divisive(mean_data_3)$estimates
    #> [1]    1  301  701 1001

    plot(bcp::bcp(mean_data_3))

`bcp` plot for multivariate mean change

# Multivariate mean and/or variance change

The true change points are 300, 700, 1000, 1300 and 1700. Some methods are plotted due to the un-retrievable change points.

    (fastcpd_result <- fastcpd::fastcpd.mv(mv_data_3, r.progress = FALSE)@cp_set)
    #> error: fastcpd_class_public.cc:85: Invalid family.
    #> error: fastcpd_class_public.cc:99: Invalid family.
    #> [1]  300  700 1000 1300 1700

    ecp::e.divisive(mv_data_3)$estimates
    #> [1]    1  301  701 1001 1301 1701 2001

    invisible(
      suppressMessages(
        capture.output(
          result_InspectChangepoint <- InspectChangepoint::inspect(
            t(mv_data_3),
            threshold = InspectChangepoint::compute.threshold(
              nrow(mv_data_3), ncol(mv_data_3)
            )
          )
        )
      )
    )
    result_InspectChangepoint$changepoints[, "location"]
    #>   [1]  300  700  701  703  705  707  708  709  711  712  714  715  717  718
    #>  [15]  720  721  723  724  726  727  729  731  733  734  736  737  739  740
    #>  [29]  742  743  744  746  747  749  750  752  753  754  755  756  758  760
    #>  [43]  762  763  765  766  767  769  770  772  773  774  775  777  779  780
    #>  [57]  782  784  786  788  790  791  793  795  797  799  801  803  804  806
    #>  [71]  808  809  810  811  813  814  816  817  818  820  821  823  825  827
    #>  [85]  828  830  831  833  835  836  837  838  840  842  843  845  846  848
    #>  [99]  849  850  852  853  854  855  856  858  859  860  862  863  865  866
    #> [113]  868  869  871  872  874  876  877  878  879  881  883  885  887  888
    #> [127]  889  891  893  894  895  897  898  900  901  903  904  906  908  909
    #> [141]  911  913  914  916  917  918  920  921  923  924  925  927  928  929
    #> [155]  931  932  934  936  937  938  939  941  942  943  945  946  947  949
    #> [169]  950  952  954  955  956  957  958  959  961  962  964  965  967  968
    #> [183]  970  972  973  974  975  977  979  981  982  984  985  986  987  988
    #> [197]  990  991  992  994  995  997  999 1000 1300 1700 1702 1703 1704 1705
    #> [211] 1706 1708 1709 1710 1712 1713 1714 1715 1717 1719 1721 1722 1723 1725
    #> [225] 1727 1729 1730 1732 1734 1735 1737 1738 1739 1741 1742 1744 1746 1748
    #> [239] 1750 1752 1753 1754 1755 1757 1758 1759 1761 1762 1763 1764 1766 1767
    #> [253] 1769 1770 1771 1773 1774 1775 1777 1779 1781 1782 1783 1785 1786 1788
    #> [267] 1789 1791 1793 1794 1796 1798 1800 1803 1804 1805 1806 1808 1809 1811
    #> [281] 1812 1814 1815 1817 1818 1819 1821 1822 1824 1825 1827 1828 1829 1831
    #> [295] 1833 1835 1836 1838 1839 1841 1843 1844 1846 1847 1848 1850 1851 1853
    #> [309] 1854 1856 1857 1858 1859 1860 1862 1863 1864 1865 1867 1869 1870 1872
    #> [323] 1873 1874 1876 1878 1879 1881 1882 1884 1885 1887 1889 1891 1893 1894
    #> [337] 1896 1898 1899 1900 1901 1902 1904 1906 1907 1909 1911 1913 1914 1916
    #> [351] 1917 1918 1919 1921 1923 1924 1925 1927 1928 1930 1932 1933 1935 1936
    #> [365] 1938 1939 1941 1942 1944 1946 1948 1950 1951 1952 1954 1956 1957 1959
    #> [379] 1961 1963 1965 1967 1968 1970 1972 1973 1974 1976 1977 1979 1981 1982
    #> [393] 1984 1985 1987 1989 1990 1992 1993 1995 1996 1998

    invisible(
      capture.output(
        result_Rbeast <- Rbeast::beast123(
          mv_data_3,
          metadata = list(whichDimIsTime = 1),
          season = "none"
        )
      )
    )
    result_Rbeast$trend$cp
    #> Warning message:
    #> In Rbeast::beast123(mv_data_3, metadata = list(whichDimIsTime = 1),  :
    #>   WARNING: If the input data is regular and ordered in time,the times of individual datapoints are determined fully by 'metadata$startTime' and 'metadata$deltaTime'. But startTime and deltaTime are missing and a default value 1 is used for both!
    #>       [,1] [,2] [,3] [,4]
    #>  [1,]  701  301  301  710
    #>  [2,] 1301 1301 1301  301
    #>  [3,]  301  701  702 1301
    #>  [4,]  814 1993 1829 1986
    #>  [5,] 1968  767 1822  785
    #>  [6,] 1994  781  810  774
    #>  [7,]  761  884  845 1912
    #>  [8,] 1873  755  798  792
    #>  [9,] 1865  747  771  879
    #> [10,] 1962  924 1700 1919

# Linear regression

The true change points are 100 and 200.

    (fastcpd_result <- fastcpd::fastcpd.lm(lm_data, r.progress = FALSE)@cp_set)
    #> [1]  97 201

    strucchange::breakpoints(y ~ . - 1, data = lm_data)$breakpoints
    #> [1] 100 201

    (segmented_result <- segmented::segmented(
      lm(
        y ~ . - 1,
        data.frame(y = lm_data$y, x = lm_data[, -1], index = seq_len(nrow(lm_data)))
      ),
      seg.Z = ~ index
    )$psi[, "Est."])
    #> [1] 233

# Logistic regression

The true change point is 300.

    (fastcpd_result <- fastcpd::fastcpd.binomial(binomial_data, r.progress = FALSE)@cp_set)
    #> [1] 302

    strucchange::breakpoints(y ~ . - 1, data = binomial_data)$breakpoints
    #> [1] 297

# Poisson regression

The true change points are 500, 800 and 1000.

    (fastcpd_result <- fastcpd::fastcpd.poisson(poisson_data, r.progress = FALSE)@cp_set)
    #> [1]  498  805 1003

    strucchange::breakpoints(y ~ . - 1, data = poisson_data)$breakpoints
    #> [1] 935

# Lasso

The true change points are 80, 200 and 320.

    (fastcpd_result <- fastcpd::fastcpd.lasso(lasso_data, r.progress = FALSE)@cp_set)
    #> [1]  79 199 321

    strucchange::breakpoints(y ~ . - 1, data = lasso_data)$breakpoints,
    #> [1]  80 200 321

# AR(3)

The true change point is 600. Some methods are plotted due to the un-retrievable change points.

    (fastcpd_result <- fastcpd::fastcpd.ar(ar_data, 3, r.progress = FALSE)@cp_set)
    #> [1] 614

    (CptNonPar_result <- CptNonPar::np.mojo(ar_data, G = floor(length(ar_data) / 6))$cpts)
    #> numeric(0)

    (segmented_result <- segmented::segmented(
      lm(
        y ~ x + 1, data.frame(y = ar_data, x = seq_along(ar_data))
      ),
      seg.Z = ~ x
    )$psi[, "Est."])
    #> [1] 690

    plot(
      mcp::mcp(
        list(y ~ 1 + ar(3), ~ 0 + ar(3)),
        data = data.frame(y = ar_data, x = seq_along(ar_data)),
        par_x = "x"
      )
    )

`mcp` plot for AR(3)

# GARCH(1, 1)

The true change point is 200.

    (fastcpd_result <- fastcpd::fastcpd.garch(garch_data, c(1, 1), r.progress = FALSE)@cp_set)
    #> error: fastcpd_class_public.cc:85: Invalid family.
    #> error: fastcpd_class_public.cc:99: Invalid family.
    #> Registered S3 method overwritten by 'quantmod':
    #>   method            from
    #>   as.zoo.data.frame zoo
    #> [1] 205

    (CptNonPar_result <- CptNonPar::np.mojo(garch_data, G = floor(length(garch_data) / 6))$cpts)
    #> [1] 206

    strucchange::breakpoints(x ~ 1, data = data.frame(x = garch_data))$breakpoints
    #> [1] NA

# VAR(2)

The true change points is 500.

    (fastcpd_result <- fastcpd::fastcpd.var(
      var_data, 2, cost_adjustment = NULL, r.progress = FALSE
    )@cp_set)
    #> error: fastcpd_class_public.cc:85: Invalid family.
    #> error: fastcpd_class_public.cc:99: Invalid family.
    #> [1] 500

    (VARDetect_result <- VARDetect::tbss(var_data)$cp)
    #> [1] "first.brk.points:"
    #>  [1] 140 196 252 308 364 420 476 532 588 644 700
    #> [1] "selected lambda1:"
    #> [1] 0.2107081
    #> [1] "selected lambda2:"
    #> [1] 0.02943525
    #> [1] "second.brk.points:"
    #> [1] 308 364 588
    #> [1] "second.brk.points:"
    #> [1] 308 476 588
    #> [1] "second.brk.points:"
    #> [1] 476 532
    #> [1] "second.brk.points:"
    #> [1] 476 532
    #> [1] "second.brk.points:"
    #> [1] 476 532
    #> no refit for the parameter estimation
    #> [1] 501

# Detection comparison using `well_log`

    well_log <- well_log[well_log > 1e5]

    result <- list(
      fastcpd = fastcpd.mean(well_log, trim = 0.003)@cp_set,
      changepoint = changepoint::cpt.mean(well_log)@cpts,
      CptNonPar =
        CptNonPar::np.mojo(well_log, G = floor(length(well_log) / 6))$cpts,
      strucchange = strucchange::breakpoints(
        y ~ 1, data = data.frame(y = well_log)
      )$breakpoints,
      ecp = ecp::e.divisive(matrix(well_log))$estimates,
      breakfast = breakfast::breakfast(well_log)$cptmodel.list[[6]]$cpts,
      wbs = wbs::wbs(well_log)$cpt$cpt.ic$mbic.penalty,
      mosum = mosum::mosum(c(well_log), G = 40)$cpts.info$cpts,
      # fpop = fpop::Fpop(well_log, length(well_log))$t.est,  # meaningless
      gfpop = gfpop::gfpop(
        data = well_log,
        mygraph = gfpop::graph(
          penalty = 2 * log(length(well_log)) * gfpop::sdDiff(well_log) ^ 2,
          type = "updown"
        ),
        type = "mean"
      )$changepoints,
      InspectChangepoint = InspectChangepoint::inspect(
        well_log,
        threshold = InspectChangepoint::compute.threshold(length(well_log), 1)
      )$changepoints[, "location"],
      jointseg = jointseg::jointSeg(well_log, K = 12)$bestBkp,
      Rbeast = Rbeast::beast(
        well_log, season = "none", print.progress = FALSE, quiet = TRUE
      )$trend$cp,
      stepR = stepR::stepFit(well_log, alpha = 0.5)$rightEnd
    )

    package_list <- sort(names(result), decreasing = TRUE)
    comparison_table <- NULL
    for (package_index in seq_along(package_list)) {
      package <- package_list[[package_index]]
      comparison_table <- rbind(
        comparison_table,
        data.frame(
          change_point = result[[package]],
          package = package,
          y_offset = (package_index - 1) * 1000
        )
      )
    }

    most_selected <- sort(table(comparison_table$change_point), decreasing = TRUE)
    most_selected <- sort(as.numeric(names(most_selected[most_selected >= 4])))
    for (i in seq_len(length(most_selected) - 1)) {
      if (most_selected[i + 1] - most_selected[i] < 2) {
        most_selected[i] <- NA
        most_selected[i + 1] <- most_selected[i + 1] - 0.5
      }
    }
    (most_selected <- most_selected[!is.na(most_selected)])
    #>  [1]    6.0  314.0  434.0  704.0  776.0 1021.0 1057.0 1347.0 1405.0 1502.0 1661.0 1842.0 2023.0 2202.0
    #> [15] 2384.5 2445.0 2507.0 2567.5 2738.0 2921.0 3094.0 3251.0 3464.0 3499.0 3622.0 3709.0 3820.0 3976.0

    ggplot2::ggplot() +
      ggplot2::geom_point(
        data = data.frame(x = seq_along(well_log), y = c(well_log)),
        ggplot2::aes(x = x, y = y)
      ) +
      ggplot2::geom_vline(
        xintercept = most_selected,
        color = "black",
        linetype = "dashed",
        alpha = 0.2
      ) +
      ggplot2::geom_point(
        data = comparison_table,
        ggplot2::aes(x = change_point, y = 50000 + y_offset, color = package),
        shape = 17,
        size = 1.9
      ) +
      ggplot2::geom_hline(
        data = comparison_table,
        ggplot2::aes(yintercept = 50000 + y_offset, color = package),
        linetype = "dashed",
        alpha = 0.1
      ) +
      ggplot2::coord_cartesian(
        ylim = c(50000 - 500, max(well_log) + 1000),
        xlim = c(-200, length(well_log) + 200),
        expand = FALSE
      ) +
      ggplot2::theme(
        panel.background = ggplot2::element_blank(),
        panel.border = ggplot2::element_rect(colour = "black", fill = NA),
        panel.grid.major = ggplot2::element_blank(),
        panel.grid.minor = ggplot2::element_blank()
      ) +
      ggplot2::xlab(NULL) + ggplot2::ylab(NULL)

Plot of the detection using `well_log` data

# Time comparison using `well_log`

    microbenchmark_result <- microbenchmark::microbenchmark(
      fastcpd = fastcpd::fastcpd.mean(well_log, trim = 0.003, r.progress = FALSE),
      changepoint = changepoint::cpt.mean(well_log, method = "PELT"),
      CptNonPar = CptNonPar::np.mojo(well_log, G = floor(length(well_log) / 6)),
      strucchange =
        strucchange::breakpoints(y ~ 1, data = data.frame(y = well_log)),
      ecp = ecp::e.divisive(matrix(well_log)),
      breakfast = breakfast::breakfast(well_log),
      wbs = wbs::wbs(well_log),
      mosum = mosum::mosum(c(well_log), G = 40),
      fpop = fpop::Fpop(well_log, nrow(well_log)),
      gfpop = gfpop::gfpop(
        data = well_log,
        mygraph = gfpop::graph(
          penalty = 2 * log(length(well_log)) * gfpop::sdDiff(well_log) ^ 2,
          type = "updown"
        ),
        type = "mean"
      ),
      InspectChangepoint = InspectChangepoint::inspect(
        well_log,
        threshold = InspectChangepoint::compute.threshold(length(well_log), 1)
      ),
      jointseg = jointseg::jointSeg(well_log, K = 12),
      Rbeast = Rbeast::beast(
        well_log, season = "none", print.progress = FALSE, quiet = TRUE
      ),
      stepR = stepR::stepFit(well_log, alpha = 0.5),
      not = not::not(well_log, contrast = "pcwsConstMean"),
      times = 10
    )
    #> Unit: milliseconds
    #>                expr          min           lq         mean       median           uq          max neval
    #>             fastcpd 8.411284e+01 8.692226e+01 1.011440e+02 1.044509e+02 1.089672e+02    118.05842    10
    #>         changepoint 3.206773e+01 3.377081e+01 6.843465e+01 3.857181e+01 5.243834e+01    244.76672    10
    #>           CptNonPar 1.827381e+04 1.901094e+04 2.002752e+04 1.985180e+04 2.076803e+04  22511.59316    10
    #>         strucchange 5.955079e+04 6.059315e+04 6.185727e+04 6.131291e+04 6.312073e+04  64638.93090    10
    #>                 ecp 7.590543e+05 7.707573e+05 7.859080e+05 7.830752e+05 8.093015e+05 810339.52140    10
    #>           breakfast 9.170992e+03 9.344041e+03 9.628236e+03 9.382078e+03 9.628663e+03  11073.79318    10
    #>                 wbs 1.139078e+02 1.145472e+02 1.178167e+02 1.166746e+02 1.201676e+02    127.27064    10
    #>               mosum 1.172847e+00 1.231747e+00 1.740727e+01 1.416854e+00 1.919586e+00    160.76997    10
    #>                fpop 2.588228e+00 2.630407e+00 4.587742e+00 2.832556e+00 3.312986e+00     18.52067    10
    #>               gfpop 5.971245e+01 6.072684e+01 6.533492e+01 6.172578e+01 6.839653e+01     87.89407    10
    #>  InspectChangepoint 1.698673e+02 1.909034e+02 2.392539e+02 2.117010e+02 3.004474e+02    329.87724    10
    #>            jointseg 2.000894e+01 2.136878e+01 2.551210e+01 2.167757e+01 2.403593e+01     47.98397    10
    #>              Rbeast 6.533998e+02 6.625203e+02 6.783586e+02 6.792646e+02 6.875840e+02    723.45376    10
    #>               stepR 2.793709e+01 2.902084e+01 4.380857e+01 3.068416e+01 3.227125e+01    164.81082    10
    #>                 not 9.599763e+01 9.701856e+01 1.028601e+02 1.012292e+02 1.049974e+02    120.73529    10

    ggplot2::autoplot(microbenchmark_result)

Plot of the running time using `well_log` data

# Related issues

- `mosum`: [#4].
- `mcp`: link 1 and link 2.
- `bcp`: [#5].
- `gfpop`: [#10].

# Acknowledgements

- Dr. Vito Muggeo, author of the `segmented` package for the tips about the piece-wise constant function.

# Appendix: all code snippets

    knitr::opts_chunk$set(
      collapse = TRUE, comment = "#>", eval = TRUE, cache = FALSE,
      warning = FALSE, fig.width = 8, fig.height = 5
    )
    # Univariate mean change
    set.seed(1)
    p <- 1
    mean_data_1 <- rbind(
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(100, p)),
      mvtnorm::rmvnorm(400, mean = rep(50, p), sigma = diag(100, p)),
      mvtnorm::rmvnorm(300, mean = rep(2, p), sigma = diag(100, p))
    )

    plot.ts(mean_data_1)
    # Univariate mean and/or variance change
    set.seed(1)
    p <- 1
    mv_data_1 <- rbind(
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(400, mean = rep(10, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(100, p)),
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(400, mean = rep(10, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(300, mean = rep(10, p), sigma = diag(100, p))
    )

    plot.ts(mv_data_1)
    # Multivariate mean change
    set.seed(1)
    p <- 3
    mean_data_3 <- rbind(
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(100, p)),
      mvtnorm::rmvnorm(400, mean = rep(50, p), sigma = diag(100, p)),
      mvtnorm::rmvnorm(300, mean = rep(2, p), sigma = diag(100, p))
    )

    plot.ts(mean_data_3)
    # Multivariate mean and/or variance change
    set.seed(1)
    p <- 4
    mv_data_3 <- rbind(
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(400, mean = rep(10, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(100, p)),
      mvtnorm::rmvnorm(300, mean = rep(0, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(400, mean = rep(10, p), sigma = diag(1, p)),
      mvtnorm::rmvnorm(300, mean = rep(10, p), sigma = diag(100, p))
    )

    plot.ts(mv_data_3)
    # Linear regression
    set.seed(1)
    n <- 300
    p <- 4
    x <- mvtnorm::rmvnorm(n, rep(0, p), diag(p))
    theta_0 <- rbind(c(1, 3.2, -1, 0), c(-1, -0.5, 2.5, -2), c(0.8, 0, 1, 2))
    y <- c(
      x[1:100, ] %*% theta_0[1, ] + rnorm(100, 0, 3),
      x[101:200, ] %*% theta_0[2, ] + rnorm(100, 0, 3),
      x[201:n, ] %*% theta_0[3, ] + rnorm(100, 0, 3)
    )
    lm_data <- data.frame(y = y, x = x)

    plot.ts(lm_data)
    # Logistic regression
    set.seed(1)
    n <- 500
    p <- 4
    x <- mvtnorm::rmvnorm(n, rep(0, p), diag(p))
    theta <- rbind(rnorm(p, 0, 1), rnorm(p, 2, 1))
    y <- c(
      rbinom(300, 1, 1 / (1 + exp(-x[1:300, ] %*% theta[1, ]))),
      rbinom(200, 1, 1 / (1 + exp(-x[301:n, ] %*% theta[2, ])))
    )
    binomial_data <- data.frame(y = y, x = x)

    plot.ts(binomial_data)
    # Poisson regression
    set.seed(1)
    n <- 1100
    p <- 3
    x <- mvtnorm::rmvnorm(n, rep(0, p), diag(p))
    delta <- rnorm(p)
    theta_0 <- c(1, 0.3, -1)
    y <- c(
      rpois(500, exp(x[1:500, ] %*% theta_0)),
      rpois(300, exp(x[501:800, ] %*% (theta_0 + delta))),
      rpois(200, exp(x[801:1000, ] %*% theta_0)),
      rpois(100, exp(x[1001:1100, ] %*% (theta_0 - delta)))
    )
    poisson_data <- data.frame(y = y, x = x)

    plot.ts(log(poisson_data$y))
    plot.ts(poisson_data[, -1])
    # Lasso
    set.seed(1)
    n <- 480
    p_true <- 6
    p <- 50
    x <- mvtnorm::rmvnorm(n, rep(0, p), diag(p))
    theta_0 <- rbind(
      runif(p_true, -5, -2),
      runif(p_true, -3, 3),
      runif(p_true, 2, 5),
      runif(p_true, -5, 5)
    )
    theta_0 <- cbind(theta_0, matrix(0, ncol = p - p_true, nrow = 4))
    y <- c(
      x[1:80, ] %*% theta_0[1, ] + rnorm(80, 0, 1),
      x[81:200, ] %*% theta_0[2, ] + rnorm(120, 0, 1),
      x[201:320, ] %*% theta_0[3, ] + rnorm(120, 0, 1),
      x[321:n, ] %*% theta_0[4, ] + rnorm(160, 0, 1)
    )
    lasso_data <- data.frame(y = y, x = x)

    plot.ts(lasso_data[, seq_len(p_true + 1)])
    # AR(3)
    set.seed(1)
    n <- 1000
    x <- rep(0, n + 3)
    for (i in 1:600) {
      x[i + 3] <- 0.6 * x[i + 2] - 0.2 * x[i + 1] + 0.1 * x[i] + rnorm(1, 0, 3)
    }
    for (i in 601:1000) {
      x[i + 3] <- 0.3 * x[i + 2] + 0.4 * x[i + 1] + 0.2 * x[i] + rnorm(1, 0, 3)
    }
    ar_data <- x[-seq_len(3)]

    plot.ts(ar_data)
    # GARCH(1, 1)
    set.seed(1)
    n <- 400
    sigma_2 <- rep(1, n + 1)
    x <- rep(0, n + 1)
    for (i in seq_len(200)) {
      sigma_2[i + 1] <- 20 + 0.5 * x[i]^2 + 0.1 * sigma_2[i]
      x[i + 1] <- rnorm(1, 0, sqrt(sigma_2[i + 1]))
    }
    for (i in 201:400) {
      sigma_2[i + 1] <- 1 + 0.1 * x[i]^2 + 0.5 * sigma_2[i]
      x[i + 1] <- rnorm(1, 0, sqrt(sigma_2[i + 1]))
    }
    garch_data <- x[-1]

    plot.ts(garch_data)
    # VAR(2)
    set.seed(1)
    n <- 800
    p <- 2
    theta_1 <- matrix(c(-0.3, 0.6, -0.5, 0.4, 0.2, 0.2, 0.2, -0.2), nrow = p)
    theta_2 <- matrix(c(0.3, -0.4, 0.1, -0.5, -0.5, -0.2, -0.5, 0.2), nrow = p)
    x <- matrix(0, n + 2, p)
    for (i in 1:500) {
      x[i + 2, ] <- theta_1 %*% c(x[i + 1, ], x[i, ]) + rnorm(p, 0, 1)
    }
    for (i in 501:n) {
      x[i + 2, ] <- theta_2 %*% c(x[i + 1, ], x[i, ]) + rnorm(p, 0, 1)
    }
    var_data <- x[-seq_len(2), ]

    plot.ts(var_data)
    (fastcpd_result <- fastcpd::fastcpd.mean(mean_data_1, r.progress = FALSE)@cp_set)
    testthat::expect_equal(fastcpd_result, c(300, 700), tolerance = 0.2)
    (CptNonPar_result <- CptNonPar::np.mojo(mean_data_1, G = floor(length(mean_data_1) / 6))$cpts)
    testthat::expect_equal(CptNonPar_result, c(300, 700), tolerance = 0.2)
    strucchange::breakpoints(y ~ 1, data = data.frame(y = mean_data_1))$breakpoints
    #> [1] 300 700
    ecp::e.divisive(mean_data_1)$estimates
    #> [1]    1  301  701 1001
    (changepoint_result <- changepoint::cpt.mean(c(mean_data_1))@cpts)
    testthat::expect_equal(changepoint_result, c(300, 1000), tolerance = 0.2)
    (breakfast_result <- breakfast::breakfast(mean_data_1)$cptmodel.list[[6]]$cpts)
    testthat::expect_equal(breakfast_result, c(300, 700), tolerance = 0.2)
    (wbs_result <- wbs::wbs(mean_data_1)$cpt$cpt.ic$mbic.penalty)
    testthat::expect_equal(wbs_result, c(300, 700), tolerance = 0.2)
    mosum::mosum(c(mean_data_1), G = 40)$cpts.info$cpts
    #> [1] 300 700
    (fpop_result <- fpop::Fpop(mean_data_1, nrow(mean_data_1))$t.est)
    testthat::expect_equal(fpop_result, c(300, 700, 1000), tolerance = 0.2)
    gfpop::gfpop(
      data = mean_data_1,
      mygraph = gfpop::graph(
        penalty = 2 * log(nrow(mean_data_1)) * gfpop::sdDiff(mean_data_1) ^ 2,
        type = "updown"
      ),
      type = "mean"
    )$changepoints
    #> [1]  300  700 1000
    invisible(
      suppressMessages(
        capture.output(
          result_InspectChangepoint <- InspectChangepoint::inspect(
            t(mean_data_1),
            threshold = InspectChangepoint::compute.threshold(
              nrow(mean_data_1), ncol(mean_data_1)
            )
          )
        )
      )
    )
    result_InspectChangepoint$changepoints[, "location"]
    #> [1] 300 700
    (jointseg_result <- jointseg::jointSeg(mean_data_1, K = 2)$bestBkp)
    testthat::expect_equal(jointseg_result, c(300, 700), tolerance = 0.2)
    Rbeast::beast(
      mean_data_1, season = "none", print.progress = FALSE, quiet = TRUE
    )$trend$cp
    #>  [1] 701 301 NaN NaN NaN NaN NaN NaN NaN NaN
    (stepR_result <- stepR::stepFit(mean_data_1, alpha = 0.5)$rightEnd)
    testthat::expect_equal(stepR_result, c(300, 700, 1000), tolerance = 0.2)
    (cpm_result <- cpm::processStream(mean_data_1, cpmType = "Student")$changePoints)
    testthat::expect_equal(cpm_result, c(299, 699), tolerance = 0.2)
    (segmented_result <- segmented::stepmented(
      as.numeric(mean_data_1), npsi = 2
    )$psi[, "Est."])
    testthat::expect_equivalent(segmented_result, c(298, 699), tolerance = 0.2)
    plot(
      mcp::mcp(
        list(y ~ 1, ~ 1, ~ 1),
        data = data.frame(y = mean_data_1, x = seq_len(nrow(mean_data_1))),
        par_x = "x"
      )
    )
    plot(not::not(mean_data_1, contrast = "pcwsConstMean"))
    plot(bcp::bcp(mean_data_1))
    (fastcpd_result <- fastcpd::fastcpd.mv(mv_data_1, r.progress = FALSE)@cp_set)
    testthat::expect_equal(fastcpd_result, c(300, 700, 1001, 1300, 1700), tolerance = 0.2)
    ecp::e.divisive(mv_data_1)$estimates
    #> [1]    1  301  701 1001 1301 1701 2001
    (changepoint_result <- changepoint::cpt.meanvar(c(mv_data_1))@cpts)
    testthat::expect_equal(changepoint_result, c(300, 2000), tolerance = 0.2)
    (CptNonPar_result <- CptNonPar::np.mojo(mv_data_1, G = floor(length(mv_data_1) / 6))$cpts)
    testthat::expect_equal(CptNonPar_result, c(333, 700, 1300), tolerance = 0.2)
    (cpm_result <- cpm::processStream(mv_data_1, cpmType = "GLR")$changePoints)
    testthat::expect_equal(cpm_result, c(293, 300, 403, 408, 618, 621, 696, 1000, 1021, 1024, 1293, 1300, 1417, 1693, 1700, 1981), tolerance = 0.2)
    invisible(
      suppressMessages(
        capture.output(
          result_InspectChangepoint <- InspectChangepoint::inspect(
            t(mv_data_1),
            threshold = InspectChangepoint::compute.threshold(
              nrow(mv_data_1), ncol(mv_data_1)
            )
          )
        )
      )
    )
    result_InspectChangepoint$changepoints[, "location"]
    #>   [1]  300  700  701  702  704  707  708  712  715  716  717  718  721  722
    #>  [15]  723  726  727  729  731  732  734  736  740  742  744  746  748  750
    #>  [29]  753  755  756  757  759  760  762  764  765  768  769  771  772  774
    #>  [43]  776  777  784  785  786  789  791  792  794  797  798  799  801  802
    #>  [57]  803  807  809  810  813  815  817  819  826  827  828  829  831  833
    #>  [71]  835  836  837  838  840  841  842  843  845  848  849  852  854  860
    #>  [85]  862  864  866  868  870  872  875  879  881  884  886  887  888  889
    #>  [99]  896  897  898  899  901  903  904  905  906  909  912  913  915  917
    #> [113]  919  921  922  923  927  928  932  934  936  937  940  944  945  947
    #> [127]  948  949  951  956  958  959  961  962  963  964  966  967  968  972
    #> [141]  974  976  978  979  986  988  990  992  995 1000 1300 1700 1702 1703
    #> [155] 1704 1705 1708 1710 1712 1714 1716 1717 1718 1720 1721 1723 1725 1726
    #> [169] 1727 1729 1731 1733 1735 1736 1737 1739 1742 1745 1747 1748 1752 1754
    #> [183] 1756 1758 1759 1760 1766 1768 1770 1771 1773 1775 1778 1782 1784 1785
    #> [197] 1790 1792 1793 1795 1796 1797 1799 1800 1802 1803 1804 1805 1806 1807
    #> [211] 1808 1809 1821 1824 1825 1827 1828 1829 1833 1835 1837 1840 1841 1842
    #> [225] 1848 1849 1851 1852 1854 1855 1857 1859 1860 1862 1863 1865 1867 1868
    #> [239] 1876 1878 1879 1880 1882 1883 1884 1886 1887 1889 1894 1898 1899 1905
    #> [253] 1906 1907 1908 1909 1912 1919 1926 1927 1928 1930 1933 1934 1935 1936
    #> [267] 1938 1940 1944 1947 1950 1952 1954 1955 1956 1960 1962 1963 1965 1966
    #> [281] 1967 1969 1970 1974 1976 1977 1978 1980 1985 1987 1988 1990 1997 1998
    Rbeast::beast(
      mv_data_1, season = "none", print.progress = FALSE, quiet = TRUE
    )$trend$cp
    #>  [1] 1794 1855 1986 1301  301  703 1981 1769 1860 1834
    plot(
      mcp::mcp(
        list(y ~ 1, ~ 1, ~ 1, ~ 1, ~ 1, ~ 1),
        data = data.frame(y = mv_data_1, x = seq_len(nrow(mv_data_1))),
        par_x = "x"
      )
    )
    plot(not::not(mv_data_1, contrast = "pcwsConstMeanVar"))
    (fastcpd_result <- fastcpd::fastcpd.mean(mean_data_3, r.progress = FALSE)@cp_set)
    testthat::expect_equal(fastcpd_result, c(300, 700), tolerance = 0.2)
    (CptNonPar_result <- CptNonPar::np.mojo(mean_data_3, G = floor(nrow(mean_data_3) / 6))$cpts)
    testthat::expect_equal(CptNonPar_result, c(300, 700), tolerance = 0.2)
    invisible(
      suppressMessages(
        capture.output(
          result_InspectChangepoint <- InspectChangepoint::inspect(
            t(mean_data_3),
            threshold = InspectChangepoint::compute.threshold(
              nrow(mean_data_3), ncol(mean_data_3)
            )
          )
        )
      )
    )
    result_InspectChangepoint$changepoints[, "location"]
    #> [1] 300 700
    (jointseg_result <- jointseg::jointSeg(mean_data_3, K = 2)$bestBkp)
    testthat::expect_equal(jointseg_result, c(300, 700), tolerance = 0.2)
    invisible(
      capture.output(
        result_Rbeast <- Rbeast::beast123(
          mean_data_3,
          metadata = list(whichDimIsTime = 1),
          season = "none"
        )
      )
    )
    result_Rbeast$trend$cp
    #> Warning message:
    #> In Rbeast::beast123(mean_data_3, metadata = list(whichDimIsTime = 1),  :
    #>   WARNING: If the input data is regular and ordered in time,the times of individual datapoints are determined fully by 'metadata$startTime' and 'metadata$deltaTime'. But startTime and deltaTime are missing and a default value 1 is used for both!
    #>       [,1] [,2] [,3]
    #>  [1,]  301  301  701
    #>  [2,]  701  701  301
    #>  [3,]  NaN  225  NaN
    #>  [4,]  NaN  684  NaN
    #>  [5,]  NaN  NaN  NaN
    #>  [6,]  NaN  NaN  NaN
    #>  [7,]  NaN  NaN  NaN
    #>  [8,]  NaN  NaN  NaN
    #>  [9,]  NaN  NaN  NaN
    #> [10,]  NaN  NaN  NaN
    strucchange::breakpoints(
      cbind(y.1, y.2, y.3) ~ 1, data = data.frame(y = mean_data_3)
    )$breakpoints
    #> [1] 300 700
    ecp::e.divisive(mean_data_3)$estimates
    #> [1]    1  301  701 1001
    plot(bcp::bcp(mean_data_3))
    (fastcpd_result <- fastcpd::fastcpd.mv(mv_data_3, r.progress = FALSE)@cp_set)
    testthat::expect_equal(fastcpd_result, c(300, 700, 1000, 1300, 1700), tolerance = 0.2)
    ecp::e.divisive(mv_data_3)$estimates
    #> [1]    1  301  701 1001 1301 1701 2001
    invisible(
      suppressMessages(
        capture.output(
          result_InspectChangepoint <- InspectChangepoint::inspect(
            t(mv_data_3),
            threshold = InspectChangepoint::compute.threshold(
              nrow(mv_data_3), ncol(mv_data_3)
            )
          )
        )
      )
    )
    result_InspectChangepoint$changepoints[, "location"]
    #>   [1]  300  700  701  703  705  707  708  709  711  712  714  715  717  718
    #>  [15]  720  721  723  724  726  727  729  731  733  734  736  737  739  740
    #>  [29]  742  743  744  746  747  749  750  752  753  754  755  756  758  760
    #>  [43]  762  763  765  766  767  769  770  772  773  774  775  777  779  780
    #>  [57]  782  784  786  788  790  791  793  795  797  799  801  803  804  806
    #>  [71]  808  809  810  811  813  814  816  817  818  820  821  823  825  827
    #>  [85]  828  830  831  833  835  836  837  838  840  842  843  845  846  848
    #>  [99]  849  850  852  853  854  855  856  858  859  860  862  863  865  866
    #> [113]  868  869  871  872  874  876  877  878  879  881  883  885  887  888
    #> [127]  889  891  893  894  895  897  898  900  901  903  904  906  908  909
    #> [141]  911  913  914  916  917  918  920  921  923  924  925  927  928  929
    #> [155]  931  932  934  936  937  938  939  941  942  943  945  946  947  949
    #> [169]  950  952  954  955  956  957  958  959  961  962  964  965  967  968
    #> [183]  970  972  973  974  975  977  979  981  982  984  985  986  987  988
    #> [197]  990  991  992  994  995  997  999 1000 1300 1700 1702 1703 1704 1705
    #> [211] 1706 1708 1709 1710 1712 1713 1714 1715 1717 1719 1721 1722 1723 1725
    #> [225] 1727 1729 1730 1732 1734 1735 1737 1738 1739 1741 1742 1744 1746 1748
    #> [239] 1750 1752 1753 1754 1755 1757 1758 1759 1761 1762 1763 1764 1766 1767
    #> [253] 1769 1770 1771 1773 1774 1775 1777 1779 1781 1782 1783 1785 1786 1788
    #> [267] 1789 1791 1793 1794 1796 1798 1800 1803 1804 1805 1806 1808 1809 1811
    #> [281] 1812 1814 1815 1817 1818 1819 1821 1822 1824 1825 1827 1828 1829 1831
    #> [295] 1833 1835 1836 1838 1839 1841 1843 1844 1846 1847 1848 1850 1851 1853
    #> [309] 1854 1856 1857 1858 1859 1860 1862 1863 1864 1865 1867 1869 1870 1872
    #> [323] 1873 1874 1876 1878 1879 1881 1882 1884 1885 1887 1889 1891 1893 1894
    #> [337] 1896 1898 1899 1900 1901 1902 1904 1906 1907 1909 1911 1913 1914 1916
    #> [351] 1917 1918 1919 1921 1923 1924 1925 1927 1928 1930 1932 1933 1935 1936
    #> [365] 1938 1939 1941 1942 1944 1946 1948 1950 1951 1952 1954 1956 1957 1959
    #> [379] 1961 1963 1965 1967 1968 1970 1972 1973 1974 1976 1977 1979 1981 1982
    #> [393] 1984 1985 1987 1989 1990 1992 1993 1995 1996 1998
    invisible(
      capture.output(
        result_Rbeast <- Rbeast::beast123(
          mv_data_3,
          metadata = list(whichDimIsTime = 1),
          season = "none"
        )
      )
    )
    result_Rbeast$trend$cp
    #> Warning message:
    #> In Rbeast::beast123(mv_data_3, metadata = list(whichDimIsTime = 1),  :
    #>   WARNING: If the input data is regular and ordered in time,the times of individual datapoints are determined fully by 'metadata$startTime' and 'metadata$deltaTime'. But startTime and deltaTime are missing and a default value 1 is used for both!
    #>       [,1] [,2] [,3] [,4]
    #>  [1,]  701  301  301  710
    #>  [2,] 1301 1301 1301  301
    #>  [3,]  301  701  702 1301
    #>  [4,]  814 1993 1829 1986
    #>  [5,] 1968  767 1822  785
    #>  [6,] 1994  781  810  774
    #>  [7,]  761  884  845 1912
    #>  [8,] 1873  755  798  792
    #>  [9,] 1865  747  771  879
    #> [10,] 1962  924 1700 1919
    (fastcpd_result <- fastcpd::fastcpd.lm(lm_data, r.progress = FALSE)@cp_set)
    testthat::expect_equal(fastcpd_result, c(97, 201), tolerance = 0.2)
    strucchange::breakpoints(y ~ . - 1, data = lm_data)$breakpoints
    #> [1] 100 201
    (segmented_result <- segmented::segmented(
      lm(
        y ~ . - 1,
        data.frame(y = lm_data$y, x = lm_data[, -1], index = seq_len(nrow(lm_data)))
      ),
      seg.Z = ~ index
    )$psi[, "Est."])
    testthat::expect_equivalent(segmented_result, c(233), tolerance = 0.2)
    (fastcpd_result <- fastcpd::fastcpd.binomial(binomial_data, r.progress = FALSE)@cp_set)
    testthat::expect_equal(fastcpd_result, 302, tolerance = 0.2)
    strucchange::breakpoints(y ~ . - 1, data = binomial_data)$breakpoints
    #> [1] 297
    (fastcpd_result <- fastcpd::fastcpd.poisson(poisson_data, r.progress = FALSE)@cp_set)
    testthat::expect_equal(fastcpd_result, c(498, 805, 1003), tolerance = 0.2)
    strucchange::breakpoints(y ~ . - 1, data = poisson_data)$breakpoints
    #> [1] 935
    (fastcpd_result <- fastcpd::fastcpd.lasso(lasso_data, r.progress = FALSE)@cp_set)
    testthat::expect_true(sum(fastcpd_result - c(79, 199, 320)) <= 1)
    strucchange::breakpoints(y ~ . - 1, data = lasso_data)$breakpoints,
    #> [1]  80 200 321
    (fastcpd_result <- fastcpd::fastcpd.ar(ar_data, 3, r.progress = FALSE)@cp_set)
    testthat::expect_equal(fastcpd_result, c(614), tolerance = 0.2)
    (CptNonPar_result <- CptNonPar::np.mojo(ar_data, G = floor(length(ar_data) / 6))$cpts)
    testthat::expect_equal(CptNonPar_result, numeric(0), tolerance = 0.2)
    (segmented_result <- segmented::segmented(
      lm(
        y ~ x + 1, data.frame(y = ar_data, x = seq_along(ar_data))
      ),
      seg.Z = ~ x
    )$psi[, "Est."])
    testthat::expect_equivalent(segmented_result, c(690), tolerance = 0.2)
    plot(
      mcp::mcp(
        list(y ~ 1 + ar(3), ~ 0 + ar(3)),
        data = data.frame(y = ar_data, x = seq_along(ar_data)),
        par_x = "x"
      )
    )
    (fastcpd_result <- fastcpd::fastcpd.garch(garch_data, c(1, 1), r.progress = FALSE)@cp_set)
    testthat::expect_equal(fastcpd_result, c(205), tolerance = 0.2)
    (CptNonPar_result <- CptNonPar::np.mojo(garch_data, G = floor(length(garch_data) / 6))$cpts)
    testthat::expect_equal(CptNonPar_result, c(206), tolerance = 0.2)
    strucchange::breakpoints(x ~ 1, data = data.frame(x = garch_data))$breakpoints
    #> [1] NA
    (fastcpd_result <- fastcpd::fastcpd.var(
      var_data, 2, cost_adjustment = NULL, r.progress = FALSE
    )@cp_set)
    testthat::expect_equal(fastcpd_result, c(500), tolerance = 0.2)
    (VARDetect_result <- VARDetect::tbss(var_data)$cp)
    testthat::expect_equal(VARDetect_result, c(501), tolerance = 0.2)
    well_log <- well_log[well_log > 1e5]

    result <- list(
      fastcpd = fastcpd.mean(well_log, trim = 0.003)@cp_set,
      changepoint = changepoint::cpt.mean(well_log)@cpts,
      CptNonPar =
        CptNonPar::np.mojo(well_log, G = floor(length(well_log) / 6))$cpts,
      strucchange = strucchange::breakpoints(
        y ~ 1, data = data.frame(y = well_log)
      )$breakpoints,
      ecp = ecp::e.divisive(matrix(well_log))$estimates,
      breakfast = breakfast::breakfast(well_log)$cptmodel.list[[6]]$cpts,
      wbs = wbs::wbs(well_log)$cpt$cpt.ic$mbic.penalty,
      mosum = mosum::mosum(c(well_log), G = 40)$cpts.info$cpts,
      # fpop = fpop::Fpop(well_log, length(well_log))$t.est,  # meaningless
      gfpop = gfpop::gfpop(
        data = well_log,
        mygraph = gfpop::graph(
          penalty = 2 * log(length(well_log)) * gfpop::sdDiff(well_log) ^ 2,
          type = "updown"
        ),
        type = "mean"
      )$changepoints,
      InspectChangepoint = InspectChangepoint::inspect(
        well_log,
        threshold = InspectChangepoint::compute.threshold(length(well_log), 1)
      )$changepoints[, "location"],
      jointseg = jointseg::jointSeg(well_log, K = 12)$bestBkp,
      Rbeast = Rbeast::beast(
        well_log, season = "none", print.progress = FALSE, quiet = TRUE
      )$trend$cp,
      stepR = stepR::stepFit(well_log, alpha = 0.5)$rightEnd
    )

    package_list <- sort(names(result), decreasing = TRUE)
    comparison_table <- NULL
    for (package_index in seq_along(package_list)) {
      package <- package_list[[package_index]]
      comparison_table <- rbind(
        comparison_table,
        data.frame(
          change_point = result[[package]],
          package = package,
          y_offset = (package_index - 1) * 1000
        )
      )
    }

    most_selected <- sort(table(comparison_table$change_point), decreasing = TRUE)
    most_selected <- sort(as.numeric(names(most_selected[most_selected >= 4])))
    for (i in seq_len(length(most_selected) - 1)) {
      if (most_selected[i + 1] - most_selected[i] < 2) {
        most_selected[i] <- NA
        most_selected[i + 1] <- most_selected[i + 1] - 0.5
      }
    }
    (most_selected <- most_selected[!is.na(most_selected)])
    #>  [1]    6.0  314.0  434.0  704.0  776.0 1021.0 1057.0 1347.0 1405.0 1502.0 1661.0 1842.0 2023.0 2202.0
    #> [15] 2384.5 2445.0 2507.0 2567.5 2738.0 2921.0 3094.0 3251.0 3464.0 3499.0 3622.0 3709.0 3820.0 3976.0
    ggplot2::ggplot() +
      ggplot2::geom_point(
        data = data.frame(x = seq_along(well_log), y = c(well_log)),
        ggplot2::aes(x = x, y = y)
      ) +
      ggplot2::geom_vline(
        xintercept = most_selected,
        color = "black",
        linetype = "dashed",
        alpha = 0.2
      ) +
      ggplot2::geom_point(
        data = comparison_table,
        ggplot2::aes(x = change_point, y = 50000 + y_offset, color = package),
        shape = 17,
        size = 1.9
      ) +
      ggplot2::geom_hline(
        data = comparison_table,
        ggplot2::aes(yintercept = 50000 + y_offset, color = package),
        linetype = "dashed",
        alpha = 0.1
      ) +
      ggplot2::coord_cartesian(
        ylim = c(50000 - 500, max(well_log) + 1000),
        xlim = c(-200, length(well_log) + 200),
        expand = FALSE
      ) +
      ggplot2::theme(
        panel.background = ggplot2::element_blank(),
        panel.border = ggplot2::element_rect(colour = "black", fill = NA),
        panel.grid.major = ggplot2::element_blank(),
        panel.grid.minor = ggplot2::element_blank()
      ) +
      ggplot2::xlab(NULL) + ggplot2::ylab(NULL)
    microbenchmark_result <- microbenchmark::microbenchmark(
      fastcpd = fastcpd::fastcpd.mean(well_log, trim = 0.003, r.progress = FALSE),
      changepoint = changepoint::cpt.mean(well_log, method = "PELT"),
      CptNonPar = CptNonPar::np.mojo(well_log, G = floor(length(well_log) / 6)),
      strucchange =
        strucchange::breakpoints(y ~ 1, data = data.frame(y = well_log)),
      ecp = ecp::e.divisive(matrix(well_log)),
      breakfast = breakfast::breakfast(well_log),
      wbs = wbs::wbs(well_log),
      mosum = mosum::mosum(c(well_log), G = 40),
      fpop = fpop::Fpop(well_log, nrow(well_log)),
      gfpop = gfpop::gfpop(
        data = well_log,
        mygraph = gfpop::graph(
          penalty = 2 * log(length(well_log)) * gfpop::sdDiff(well_log) ^ 2,
          type = "updown"
        ),
        type = "mean"
      ),
      InspectChangepoint = InspectChangepoint::inspect(
        well_log,
        threshold = InspectChangepoint::compute.threshold(length(well_log), 1)
      ),
      jointseg = jointseg::jointSeg(well_log, K = 12),
      Rbeast = Rbeast::beast(
        well_log, season = "none", print.progress = FALSE, quiet = TRUE
      ),
      stepR = stepR::stepFit(well_log, alpha = 0.5),
      not = not::not(well_log, contrast = "pcwsConstMean"),
      times = 10
    )
    #> Unit: milliseconds
    #>                expr          min           lq         mean       median           uq          max neval
    #>             fastcpd 8.411284e+01 8.692226e+01 1.011440e+02 1.044509e+02 1.089672e+02    118.05842    10
    #>         changepoint 3.206773e+01 3.377081e+01 6.843465e+01 3.857181e+01 5.243834e+01    244.76672    10
    #>           CptNonPar 1.827381e+04 1.901094e+04 2.002752e+04 1.985180e+04 2.076803e+04  22511.59316    10
    #>         strucchange 5.955079e+04 6.059315e+04 6.185727e+04 6.131291e+04 6.312073e+04  64638.93090    10
    #>                 ecp 7.590543e+05 7.707573e+05 7.859080e+05 7.830752e+05 8.093015e+05 810339.52140    10
    #>           breakfast 9.170992e+03 9.344041e+03 9.628236e+03 9.382078e+03 9.628663e+03  11073.79318    10
    #>                 wbs 1.139078e+02 1.145472e+02 1.178167e+02 1.166746e+02 1.201676e+02    127.27064    10
    #>               mosum 1.172847e+00 1.231747e+00 1.740727e+01 1.416854e+00 1.919586e+00    160.76997    10
    #>                fpop 2.588228e+00 2.630407e+00 4.587742e+00 2.832556e+00 3.312986e+00     18.52067    10
    #>               gfpop 5.971245e+01 6.072684e+01 6.533492e+01 6.172578e+01 6.839653e+01     87.89407    10
    #>  InspectChangepoint 1.698673e+02 1.909034e+02 2.392539e+02 2.117010e+02 3.004474e+02    329.87724    10
    #>            jointseg 2.000894e+01 2.136878e+01 2.551210e+01 2.167757e+01 2.403593e+01     47.98397    10
    #>              Rbeast 6.533998e+02 6.625203e+02 6.783586e+02 6.792646e+02 6.875840e+02    723.45376    10
    #>               stepR 2.793709e+01 2.902084e+01 4.380857e+01 3.068416e+01 3.227125e+01    164.81082    10
    #>                 not 9.599763e+01 9.701856e+01 1.028601e+02 1.012292e+02 1.049974e+02    120.73529    10
    ggplot2::autoplot(microbenchmark_result)