The concept of the empirical CDF (ECDF) of a sample is very simple. First, the value of the ECDF below the minimum observation is $0$ and its value above the maximum observation is $1.$ Second, sort the data from smallest to largest. If there are $n$ observations (all distinct), then the ECDF jumps up by $1/n$ at each observation. If there are ties, the jump is $d/n$ for $d$ values tied at the same value. In R, the expression `ecdf` does the work. (You might want to read the R documentation for `ecdf`.) For moderate and large sample sizes the ECDF is often a good approximation of the distribution of the population from which the data are randomly sampled (shown in red in the plots below). Examples (in R): set.seed(813) x = runif(50, 0, 10); plot(ecdf(x)); rug(x) curve(punif(x, 0, 10), add=T, col="red", n=10001) [![enter image description here][1]][1] set.seed(2019) x = rpois(10, 3); plot(ecdf(x)) curve(ppois(x, 3), add=T, col="red", n = 10001) [![enter image description here][2]][2] set.seed(1066) x = rexp(5000, 1/10); plot(ecdf(x)) curve(pexp(x, 1/10), add-T, col="red") [![enter image description here][3]][3] _Note:_ Q-Q plots (with theoretical and sample quantiles) often amount to ECDF plots with scales suitably distorted so that the population CDF if a straight line. ---------------------------------------------- **Addendum** per @whuber Comment: For a small dataset from a gamma distribution, we begin by showing a histogram of the data along with the true density function (left) and an ECDF of the data along with the true CDF (right). For illustration, I chose a small sample so that there will be a clear distinction between exact curves (blue) and estimated ones (red). set.seed(814) x = rgamma(100, 10, .2) par(mfrow=c(1,2)) hist(x, prob=T, ylim=c(0,.03)) curve(dgamma(x, 10, .2), add=T, col="blue") plot(ecdf(x), pch=".") curve(pgamma(x, 10, .2), add=T, col="blue") par(mfrow=c(1,1)) [![enter image description here][4]][4] If the true population distribution is not known, its density function can be estimate by a kernel density estimator (KDE). We use the default KDE in R. The output is two vectors: x-values and y-values for plotting. These vectors are summarized below, and the first six entries in each vector are shown. density(x) Call: density.default(x = x) Data: x (100 obs.); Bandwidth 'bw' = 5.494 x y Min. : 2.599 Min. :9.031e-06 1st Qu.: 32.251 1st Qu.:9.730e-04 Median : 61.902 Median :4.177e-03 Mean : 61.902 Mean :8.423e-03 3rd Qu.: 91.554 3rd Qu.:1.602e-02 Max. :121.205 Max. :2.527e-02 head(density(x)$x) [1] 2.599014 2.831120 3.063227 3.295333 3.527439 3.759546 head(density(x)$y) [1] 9.030655e-06 1.029092e-05 1.171087e-05 1.327874e-05 1.500377e-05 1.701109e-05 The points in the y-vector are scaled so that the curve enclosed by the KDE will be (almost exactly) 1. The KDE vectors can be used to estimate the CDF. Plotting points are `x.k = ecdf(x)$x a` and `y.k = cumsum(ecdf(x)$y)/sum(ecdf(x)$y)`. Here are plots of the histogram of `x` along with the KDE, and the ECDF along with the CDF as estimated via the KDE. x.k = density(x)$x y.k = cumsum(density(x)$y)/sum(density(x)$y) par(mfrow=c(1,2)) hist(x, prob=T) lines(density(x), col="red") plot(ecdf(x), pch=".") lines(x.k, y.k, col="red") par(mfrow=c(1,1)) [![enter image description here][5]][5] [1]: https://i.sstatic.net/r0aC0.png [2]: https://i.sstatic.net/XRtJE.png [3]: https://i.sstatic.net/3yrs5.png [4]: https://i.sstatic.net/jSZNM.png [5]: https://i.sstatic.net/RCMpc.png