[
  {
    "objectID": "pages/selection_psus.html",
    "href": "pages/selection_psus.html",
    "title": "Selection of PSUs",
    "section": "",
    "text": "In the sections below, we draw primary sampling units (PSUs) using probability proportional to size (PPS) sampling techniques implemented in the Sample class. The class Sample has two main methods that is inclusion_probs and select. The method inclusion_probs() computes the probability of selection and select() draws the random samples.\nThe following will illustrate the use of samplics for sample selection. For the illustration,\nThis example is not meant to be exhaustif. There are many use cases that are not covered in this tutorial. For example, some PSUs may be segmented due to their size and segments selected in a subsequent step. Segment selection can be done with Samplics in a similar way as the PSUs selection, with PPS or SRS, after the segements have been created by the user.\nFirst, let us import the python packages necessary to run the tutorial.\nimport numpy as np \nfrom samplics.datasets import load_psu_frame\n\nfrom samplics import SelectMethod\nfrom samplics.sampling import SampleSelection",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Selection",
      "Selection of PSUs"
    ]
  },
  {
    "objectID": "pages/selection_psus.html#sample-dataset",
    "href": "pages/selection_psus.html#sample-dataset",
    "title": "Selection of PSUs",
    "section": "Sample Dataset",
    "text": "Sample Dataset\nThe file sample_frame.csv - shown below - contains synthetic data of 100 clusters classified by region (East, North, South and West). Clusters represent a group of households. In the file, each cluster has an associated number of households (number_households) and a status variable indicating whether the cluster is in scope or not.\nThis synthetic data represents a simplified version of enumeration areas (EAs) frames found in many countries and used by major household survey programs such as the Demographic and Health Surveys (DHS), the Population-based HIV Impact Assessment (PHIA) surveys and the Multiple Cluster Indicator Surveys (MICS).\n\npsu_frame_dict = load_psu_frame()\npsu_frame = psu_frame_dict[\"data\"]\npsu_frame.head(25)\n\n\n\n\n\n\n\n\ncluster\nregion\nnumber_households_census\ncluster_status\ncomment\n\n\n\n\n0\n1\nNorth\n105\n1\nNaN\n\n\n1\n2\nNorth\n85\n1\nNaN\n\n\n2\n3\nNorth\n95\n1\nNaN\n\n\n3\n4\nNorth\n75\n1\nNaN\n\n\n4\n5\nNorth\n120\n1\nNaN\n\n\n5\n6\nNorth\n90\n1\nNaN\n\n\n6\n7\nNorth\n130\n1\nNaN\n\n\n7\n8\nNorth\n55\n1\nNaN\n\n\n8\n9\nNorth\n30\n1\nNaN\n\n\n9\n10\nNorth\n600\n1\ndue to a large building\n\n\n10\n11\nSouth\n25\n1\nNaN\n\n\n11\n12\nSouth\n250\n1\nNaN\n\n\n12\n13\nSouth\n105\n1\nNaN\n\n\n13\n14\nSouth\n75\n1\nNaN\n\n\n14\n15\nSouth\n205\n1\nNaN\n\n\n15\n16\nSouth\n190\n1\nNaN\n\n\n16\n17\nSouth\n95\n1\nNaN\n\n\n17\n18\nSouth\n85\n1\nNaN\n\n\n18\n19\nSouth\n50\n1\nNaN\n\n\n19\n20\nSouth\n110\n1\nNaN\n\n\n20\n21\nSouth\n130\n1\nNaN\n\n\n21\n22\nSouth\n180\n1\nNaN\n\n\n22\n23\nSouth\n65\n1\nNaN\n\n\n23\n24\nSouth\n75\n1\nNaN\n\n\n24\n25\nSouth\n95\n1\nNaN\n\n\n\n\n\n\n\nOften, sampling frames are not available for the sampling units of interest. For example, most countries do not have a list of all households or people living in the country. Even if such frames exist, it may not be operationally and financially feasible to directly select sampling units without any form of clustering.\nHence, stage sampling is a common strategy used by large household national surveys for selecting samples of households and people. At the first stage, geographic or administrative clusters of households are selected. At the second stage, a frame of households is created from the selected clusters and a sample of households is selected. At the third stage (if applicable), a sample of people is selected from the households in the sample. This is a high level description of the process; usually implementations are much less straightforward and may require many adjustments to address complexities.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Selection",
      "Selection of PSUs"
    ]
  },
  {
    "objectID": "pages/selection_psus.html#psu-probability-of-selection",
    "href": "pages/selection_psus.html#psu-probability-of-selection",
    "title": "Selection of PSUs",
    "section": "PSU Probability of Selection",
    "text": "PSU Probability of Selection\nAt the first stage, we use the proportional to size (pps) method to select a random sample of clusters. The measure of size is the number of households (number_households) as provided in the psu sampling frame. The sample is stratified by region. The probabilities, for stratified pps, is obtained as follow: \\[\\begin{equation} p_{hi} = \\frac{n_h M_{hi}}{\\sum_{i=1}^{N_h}{M_{hi}}} \\end{equation}\\] where \\(p_{hi}\\) is the probability of selection for unit \\(i\\) from stratum \\(h\\), \\(M_{hi}\\) is the measure of size (mos), \\(n_h\\) and \\(N_h\\) are the sample size and the total number of clusters in stratum \\(h\\), respectively.\n\n\n\n\n\n\nImportant\n\n\n\nThe PPS method is used in many surveys not just for multistage household surveys.\nFor example, in business surveys, establishments can greatly vary in size; hence pps methods are often use to select samples. Simarly, facility-based surveys can benefit from pps methods when frames with measures of size are available.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Selection",
      "Selection of PSUs"
    ]
  },
  {
    "objectID": "pages/selection_psus.html#psu-sample-size",
    "href": "pages/selection_psus.html#psu-sample-size",
    "title": "Selection of PSUs",
    "section": "PSU Sample size",
    "text": "PSU Sample size\nFor a stratified sampling design, the sample size is provided using a Python dictionary. Python dictionaries allow us to pair the strata with the sample sizes. Let’s say that we want to select 3 clusters from stratum East, 2 from West, 2 from North and 3 from South. The snippet of code below demonstrates how to create the Python dictionary. Note that it is important to correctly spell out the keys of the dictionary which corresponds to the values of the variable stratum (in our case it’s region).\n\npsu_sample_size = {\"East\":3, \"West\": 2, \"North\": 2, \"South\": 3}\n\nprint(f\"\\nThe sample size per domain is:\\n {psu_sample_size}\\n\")\n\n\nThe sample size per domain is:\n {'East': 3, 'West': 2, 'North': 2, 'South': 3}\n\n\n\nThe function array_to_dict() converts an array to a dictionnary by pairing the values of the array to their frequency. We can use this function to calculates the number of clusters per stratum and store the result in a Python dictionnary. Then, we modify the values of the dictionnary to create the sample size dictionnary.\nIf some of the clusters are certainties then an exception will be raised. Hence, the user will have to manually handle the certaininties. Better handling of certainties is planned for future versions of the library samplics.\n\nfrom samplics import array_to_dict\n\nframe_size = array_to_dict(psu_frame[\"region\"])\nprint(f\"\\nThe number of clusters per stratum is:\\n {frame_size}\")\n\n\nThe number of clusters per stratum is:\n {'East': np.int64(25), 'North': np.int64(10), 'South': np.int64(20), 'West': np.int64(45)}\n\n\n\npsu_sample_size = frame_size.copy()\npsu_sample_size[\"East\"] = 3\npsu_sample_size[\"North\"] = 2\npsu_sample_size[\"South\"] = 3\npsu_sample_size[\"West\"] = 2\nprint(f\"\\nThe sample size per stratum is:\\n {psu_sample_size}\\n\")\n\n\nThe sample size per stratum is:\n {'East': 3, 'North': 2, 'South': 3, 'West': 2}\n\n\n\n\nstage1_design = SampleSelection(method=SelectMethod.pps_sys, strat=True, wr=False)\n\npsu_frame[\"psu_prob\"] = stage1_design.inclusion_probs(\n    psu_frame[\"cluster\"], \n    psu_sample_size, \n    psu_frame[\"region\"],\n    psu_frame[\"number_households_census\"],\n    )\n\nnb_obs = 15\nprint(f\"\\nFirst {nb_obs} observations of the PSU frame \\n\")\npsu_frame.head(nb_obs)\n\n\nFirst 15 observations of the PSU frame \n\n\n\n\n\n\n\n\n\n\ncluster\nregion\nnumber_households_census\ncluster_status\ncomment\npsu_prob\n\n\n\n\n0\n1\nNorth\n105\n1\nNaN\n0.151625\n\n\n1\n2\nNorth\n85\n1\nNaN\n0.122744\n\n\n2\n3\nNorth\n95\n1\nNaN\n0.137184\n\n\n3\n4\nNorth\n75\n1\nNaN\n0.108303\n\n\n4\n5\nNorth\n120\n1\nNaN\n0.173285\n\n\n5\n6\nNorth\n90\n1\nNaN\n0.129964\n\n\n6\n7\nNorth\n130\n1\nNaN\n0.187726\n\n\n7\n8\nNorth\n55\n1\nNaN\n0.079422\n\n\n8\n9\nNorth\n30\n1\nNaN\n0.043321\n\n\n9\n10\nNorth\n600\n1\ndue to a large building\n0.866426\n\n\n10\n11\nSouth\n25\n1\nNaN\n0.027523\n\n\n11\n12\nSouth\n250\n1\nNaN\n0.275229\n\n\n12\n13\nSouth\n105\n1\nNaN\n0.115596\n\n\n13\n14\nSouth\n75\n1\nNaN\n0.082569\n\n\n14\n15\nSouth\n205\n1\nNaN\n0.225688",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Selection",
      "Selection of PSUs"
    ]
  },
  {
    "objectID": "pages/selection_psus.html#psu-selection",
    "href": "pages/selection_psus.html#psu-selection",
    "title": "Selection of PSUs",
    "section": "PSU Selection",
    "text": "PSU Selection\nIn this section, we select a sample of psus using pps methods. In the section above, we have calculated the probabilities of selection. That step is not necessary when using samplics. We can use the method select() to calculate the probability of selection and select the sample, in one run. As shown below, select() method returns a tuple of three arrays.\n* The first array indicates the selected units (i.e. psu_sample = 1 if selected, and 0 if not selected).\n* The second array provides the number of hits, useful when the sample is selected with replacement.\n* The third array is the probability of selection.\n\n\n\n\n\n\nNote\n\n\n\nnp.random.seed() fixes the random seed to allow us to reproduce the random selection.\n\n\n\nnp.random.seed(23)\n\npsu_frame[\"psu_sample\"], psu_frame[\"psu_hits\"], psu_frame[\"psu_probs\"] = \\\n    stage1_design.select(\n        psu_frame[\"cluster\"], \n        psu_sample_size, \n        psu_frame[\"region\"], \n        psu_frame[\"number_households_census\"]\n    )\n    \npsu_frame.to_csv(\"./psu_frame.csv\")\n\nprint(\n    \"\\nFirst 15 obs of the PSU frame with the sampling information\\n\"\n    )\npsu_frame[\n    [\"cluster\", \"region\", \"psu_prob\", \"psu_sample\", \"psu_hits\", \"psu_probs\"]\n    ].head(15)\n\n\nFirst 15 obs of the PSU frame with the sampling information\n\n\n\n\n\n\n\n\n\n\ncluster\nregion\npsu_prob\npsu_sample\npsu_hits\npsu_probs\n\n\n\n\n0\n1\nNorth\n0.151625\n0\n0\n0.151625\n\n\n1\n2\nNorth\n0.122744\n0\n0\n0.122744\n\n\n2\n3\nNorth\n0.137184\n0\n0\n0.137184\n\n\n3\n4\nNorth\n0.108303\n0\n0\n0.108303\n\n\n4\n5\nNorth\n0.173285\n0\n0\n0.173285\n\n\n5\n6\nNorth\n0.129964\n0\n0\n0.129964\n\n\n6\n7\nNorth\n0.187726\n1\n1\n0.187726\n\n\n7\n8\nNorth\n0.079422\n0\n0\n0.079422\n\n\n8\n9\nNorth\n0.043321\n0\n0\n0.043321\n\n\n9\n10\nNorth\n0.866426\n1\n1\n0.866426\n\n\n10\n11\nSouth\n0.027523\n0\n0\n0.027523\n\n\n11\n12\nSouth\n0.275229\n0\n0\n0.275229\n\n\n12\n13\nSouth\n0.115596\n0\n0\n0.115596\n\n\n13\n14\nSouth\n0.082569\n0\n0\n0.082569\n\n\n14\n15\nSouth\n0.225688\n0\n0\n0.225688\n\n\n\n\n\n\n\nThe default setting sample_only=False returns the entire frame. We can easily reduce the output data to the sample by filtering i.e. psu_sample == 1. However, if we are only interested in the sample, we could use sample_only=True when calling select(). This will reduce the output data to the sampled units and to_dataframe=true will convert the data to a pandas dataframe (pd.DataFrame). Note that the columns in the dataframe will be reduced to the minimum.\n\nnp.random.seed(23)\n\npsu_sample = stage1_design.select(\n    psu_frame[\"cluster\"], \n    psu_sample_size, \n    psu_frame[\"region\"], \n    psu_frame[\"number_households_census\"],\n    to_dataframe = True,\n    sample_only = True\n    )\n\nprint(\"\\nPSU sample without the non-sampled units\\n\")\npsu_sample\n\n\nPSU sample without the non-sampled units\n\n\n\n\n\n\n\n\n\n\n_samp_unit\n_stratum\n_mos\n_sample\n_hits\n_probs\n\n\n\n\n0\n7\nNorth\n130\n1\n1\n0.187726\n\n\n1\n10\nNorth\n600\n1\n1\n0.866426\n\n\n2\n16\nSouth\n190\n1\n1\n0.209174\n\n\n3\n24\nSouth\n75\n1\n1\n0.082569\n\n\n4\n29\nSouth\n200\n1\n1\n0.220183\n\n\n5\n34\nEast\n305\n1\n1\n0.210587\n\n\n6\n45\nEast\n450\n1\n1\n0.310702\n\n\n7\n52\nEast\n700\n1\n1\n0.483314\n\n\n8\n64\nWest\n300\n1\n1\n0.091673\n\n\n9\n86\nWest\n280\n1\n1\n0.085561\n\n\n\n\n\n\n\nThe systematic selection method can be implemented with or without replacement. The other samplics algorithms for selecting sample with unequal probablities of selection are Brewer, Hanurav-Vijayan (hv), Murphy, and Rao-Sampford (rs) methods. As shown below, all these sampling techniques can be specified when extentiating a Sample class; then call select() to draw samples.\n\nSample(method=SelectMethod.pps_sys, wr=True)\nSample(method=SelectMethod.pps_sys, wr=False)\nSample(method=SelectMethod.pps_brewer, wr=False)\nSample(method=SelectMethod.pps_hv, wr=False) # Hanurav-Vijayan method\nSample(method=SelectMethod.pps_murphy, wr=False)\nSample(method=SelectMethod.pps_rs, wr=False) # Rao-Sampford method\n\nFor example, if we wanted to select the sample using the Rao-Sampford method, we could use the following snippet of code.\n\nnp.random.seed(23)\n\nstage1_sampford = SampleSelection(\n    method=SelectMethod.pps_rs, \n    strat=True, \n    wr=False\n    )\n\npsu_sample_sampford = stage1_sampford.select(\n    psu_frame[\"cluster\"], \n    psu_sample_size, \n    psu_frame[\"region\"], \n    psu_frame[\"number_households_census\"],\n    to_dataframe=True,\n    sample_only=False\n    )\n\npsu_sample_sampford\n\n\n\n\n\n\n\n\n_samp_unit\n_stratum\n_mos\n_sample\n_hits\n_probs\n\n\n\n\n0\n1\nNorth\n105\n0\n0\n0.151625\n\n\n1\n2\nNorth\n85\n0\n0\n0.122744\n\n\n2\n3\nNorth\n95\n1\n1\n0.137184\n\n\n3\n4\nNorth\n75\n0\n0\n0.108303\n\n\n4\n5\nNorth\n120\n0\n0\n0.173285\n\n\n...\n...\n...\n...\n...\n...\n...\n\n\n95\n96\nWest\n95\n1\n1\n0.029030\n\n\n96\n97\nWest\n40\n0\n0\n0.012223\n\n\n97\n98\nWest\n105\n0\n0\n0.032086\n\n\n98\n99\nWest\n320\n0\n0\n0.097785\n\n\n99\n100\nWest\n200\n0\n0\n0.061115\n\n\n\n\n100 rows × 6 columns",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Selection",
      "Selection of PSUs"
    ]
  },
  {
    "objectID": "pages/estimation_rep.html",
    "href": "pages/estimation_rep.html",
    "title": "Replicated-based Estimation",
    "section": "",
    "text": "Samplics’s class TaylorEstimator uses replicate-based methods (bootstrap, brr/fay, and jackknife) to estimate the variance of population parameters.\n\nBootstrap\n\nfrom samplics.datasets import load_nhanes2brr, load_nhanes2jk, load_nmihs\nfrom samplics.estimation import ReplicateEstimator\n\nfrom samplics.utils.types import PopParam, RepMethod\n\n\n# Load NMIHS sample data\nnmihs_dict = load_nmihs()\nnmihs = nmihs_dict[\"data\"]\n\nnmihs.head(15)\n\n\n\n\n\n\n\n\nfinalwgt\nbirth_weight\nbsrw1\nbsrw2\nbsrw3\nbsrw4\nbsrw5\nbsrw6\nbsrw7\nbsrw8\n...\nbsrw41\nbsrw42\nbsrw43\nbsrw44\nbsrw45\nbsrw46\nbsrw47\nbsrw48\nbsrw49\nbsrw50\n\n\n\n\n0\n24.67243\n1270\n49.403603\n0.000000\n49.403603\n0.000000\n24.701801\n49.403603\n24.701801\n0.000000\n...\n0.000000\n0.000000\n0.000000\n0.000000\n49.403603\n0.000000\n74.105408\n49.403603\n24.701801\n49.403603\n\n\n1\n23.56827\n879\n23.596327\n47.192654\n0.000000\n0.000000\n47.192654\n23.596327\n47.192654\n23.596327\n...\n47.192654\n23.596327\n0.000000\n47.192654\n23.596327\n47.192654\n23.596327\n47.192654\n23.596327\n23.596327\n\n\n2\n24.67243\n794\n24.701801\n0.000000\n24.701801\n0.000000\n0.000000\n24.701801\n0.000000\n0.000000\n...\n24.701801\n0.000000\n0.000000\n24.701801\n24.701801\n24.701801\n0.000000\n49.403603\n24.701801\n98.807205\n\n\n3\n20.33146\n1446\n40.711327\n0.000000\n0.000000\n20.355663\n40.711327\n20.355663\n0.000000\n20.355663\n...\n0.000000\n20.355663\n61.066994\n61.066994\n40.711327\n40.711327\n20.355663\n20.355663\n81.422653\n40.711327\n\n\n4\n21.83328\n830\n21.859272\n21.859272\n0.000000\n0.000000\n0.000000\n21.859272\n0.000000\n21.859272\n...\n65.577812\n0.000000\n21.859272\n21.859272\n21.859272\n0.000000\n21.859272\n0.000000\n0.000000\n0.000000\n\n\n5\n23.56827\n1304\n70.788986\n23.596327\n23.596327\n23.596327\n0.000000\n23.596327\n47.192654\n0.000000\n...\n47.192654\n47.192654\n23.596327\n23.596327\n23.596327\n23.596327\n0.000000\n23.596327\n23.596327\n0.000000\n\n\n6\n18.67915\n1106\n18.701387\n56.104160\n0.000000\n0.000000\n18.701387\n18.701387\n18.701387\n0.000000\n...\n18.701387\n0.000000\n18.701387\n0.000000\n18.701387\n18.701387\n18.701387\n18.701387\n18.701387\n37.402775\n\n\n7\n24.63370\n1418\n24.663025\n49.326050\n0.000000\n24.663025\n0.000000\n24.663025\n0.000000\n24.663025\n...\n24.663025\n0.000000\n0.000000\n0.000000\n49.326050\n49.326050\n24.663025\n0.000000\n24.663025\n0.000000\n\n\n8\n20.33146\n1474\n0.000000\n40.711327\n40.711327\n0.000000\n20.355663\n0.000000\n0.000000\n40.711327\n...\n40.711327\n0.000000\n20.355663\n20.355663\n20.355663\n20.355663\n0.000000\n20.355663\n20.355663\n20.355663\n\n\n9\n20.33146\n454\n0.000000\n20.355663\n20.355663\n20.355663\n61.066994\n0.000000\n0.000000\n40.711327\n...\n0.000000\n61.066994\n0.000000\n20.355663\n0.000000\n20.355663\n20.355663\n20.355663\n81.422653\n0.000000\n\n\n10\n24.67243\n1380\n0.000000\n24.701801\n24.701801\n24.701801\n0.000000\n49.403603\n0.000000\n24.701801\n...\n0.000000\n49.403603\n49.403603\n0.000000\n74.105408\n24.701801\n0.000000\n24.701801\n98.807205\n49.403603\n\n\n11\n20.33146\n539\n61.066994\n20.355663\n20.355663\n0.000000\n20.355663\n20.355663\n40.711327\n40.711327\n...\n20.355663\n40.711327\n20.355663\n40.711327\n61.066994\n0.000000\n0.000000\n20.355663\n20.355663\n20.355663\n\n\n12\n24.63370\n1021\n49.326050\n24.663025\n98.652100\n24.663025\n0.000000\n73.989075\n24.663025\n0.000000\n...\n24.663025\n0.000000\n49.326050\n0.000000\n24.663025\n73.989075\n73.989075\n24.663025\n24.663025\n0.000000\n\n\n13\n21.83328\n1049\n65.577812\n21.859272\n21.859272\n21.859272\n0.000000\n65.577812\n21.859272\n21.859272\n...\n0.000000\n65.577812\n0.000000\n0.000000\n21.859272\n43.718544\n21.859272\n43.718544\n0.000000\n21.859272\n\n\n14\n18.67915\n1134\n0.000000\n18.701387\n37.402775\n0.000000\n37.402775\n0.000000\n0.000000\n56.104160\n...\n18.701387\n18.701387\n0.000000\n18.701387\n0.000000\n56.104160\n0.000000\n56.104160\n0.000000\n56.104160\n\n\n\n\n15 rows × 52 columns\n\n\n\nLet’s estimate the average birth weight using the bootstrap weights.\n\n# rep_wgt_boot = nmihsboot.loc[:, \"bsrw1\":\"bsrw50\"]\n\nbirthwgt = ReplicateEstimator(RepMethod.bootstrap, PopParam.mean).estimate(\n    y=nmihs[\"birth_weight\"],\n    samp_weight=nmihs[\"finalwgt\"],\n    rep_weights=nmihs.loc[:, \"bsrw1\":\"bsrw50\"],\n    remove_nan=True,\n)\n\nprint(birthwgt)\n\nSAMPLICS - Estimation of Mean\n\nNumber of strata: None\nNumber of psus: None\nDegree of freedom: 49\n\n       MEAN        SE         LCI         UCI       CV\n2679.127143 31.053792 2616.722212 2741.532074 0.011591\n\n\n\n\nBalanced repeated replication (BRR)\n\n# Load NMIHS sample data\nnhanes2brr_dict = load_nhanes2brr()\nnhanes2brr = nhanes2brr_dict[\"data\"]\n\nnhanes2brr.head(15)\n\n\n\n\n\n\n\n\nheight\nweight\nfinalwgt\nbrr_1\nbrr_2\nbrr_3\nbrr_4\nbrr_5\nbrr_6\nbrr_7\n...\nbrr_23\nbrr_24\nbrr_25\nbrr_26\nbrr_27\nbrr_28\nbrr_29\nbrr_30\nbrr_31\nbrr_32\n\n\n\n\n0\n174.59801\n62.480000\n8995\n0\n17990\n17990\n0\n17990\n0\n0\n...\n17990\n0\n0\n17990\n17990\n0\n17990\n0\n0\n17990\n\n\n1\n152.29700\n48.759998\n25964\n0\n51928\n51928\n0\n51928\n0\n0\n...\n51928\n0\n0\n51928\n51928\n0\n51928\n0\n0\n51928\n\n\n2\n164.09801\n67.250000\n8752\n0\n17504\n17504\n0\n17504\n0\n0\n...\n17504\n0\n0\n17504\n17504\n0\n17504\n0\n0\n17504\n\n\n3\n162.59801\n94.459999\n4310\n0\n8620\n8620\n0\n8620\n0\n0\n...\n8620\n0\n0\n8620\n8620\n0\n8620\n0\n0\n8620\n\n\n4\n163.09801\n74.279999\n9011\n0\n18022\n18022\n0\n18022\n0\n0\n...\n18022\n0\n0\n18022\n18022\n0\n18022\n0\n0\n18022\n\n\n5\n147.09801\n66.000000\n4310\n0\n8620\n8620\n0\n8620\n0\n0\n...\n8620\n0\n0\n8620\n8620\n0\n8620\n0\n0\n8620\n\n\n6\n153.89799\n54.549999\n3201\n0\n6402\n6402\n0\n6402\n0\n0\n...\n6402\n0\n0\n6402\n6402\n0\n6402\n0\n0\n6402\n\n\n7\n160.00000\n58.970001\n25386\n0\n50772\n50772\n0\n50772\n0\n0\n...\n50772\n0\n0\n50772\n50772\n0\n50772\n0\n0\n50772\n\n\n8\n164.00000\n68.949997\n12102\n0\n24204\n24204\n0\n24204\n0\n0\n...\n24204\n0\n0\n24204\n24204\n0\n24204\n0\n0\n24204\n\n\n9\n176.59801\n65.430000\n4312\n0\n8624\n8624\n0\n8624\n0\n0\n...\n8624\n0\n0\n8624\n8624\n0\n8624\n0\n0\n8624\n\n\n10\n156.19901\n76.769997\n4031\n0\n8062\n8062\n0\n8062\n0\n0\n...\n8062\n0\n0\n8062\n8062\n0\n8062\n0\n0\n8062\n\n\n11\n170.09801\n58.400002\n3628\n0\n7256\n7256\n0\n7256\n0\n0\n...\n7256\n0\n0\n7256\n7256\n0\n7256\n0\n0\n7256\n\n\n12\n151.79700\n65.769997\n28590\n0\n57180\n57180\n0\n57180\n0\n0\n...\n57180\n0\n0\n57180\n57180\n0\n57180\n0\n0\n57180\n\n\n13\n154.19901\n48.540001\n22754\n0\n45508\n45508\n0\n45508\n0\n0\n...\n45508\n0\n0\n45508\n45508\n0\n45508\n0\n0\n45508\n\n\n14\n171.09801\n73.029999\n7119\n0\n14238\n14238\n0\n14238\n0\n0\n...\n14238\n0\n0\n14238\n14238\n0\n14238\n0\n0\n14238\n\n\n\n\n15 rows × 35 columns\n\n\n\nLet’s estimate the average birth weight using the BRR weights.\n\nbrr = ReplicateEstimator(RepMethod.brr, PopParam.ratio)\n\nratio_wgt_hgt = brr.estimate(\n    y=nhanes2brr[\"weight\"],\n    samp_weight=nhanes2brr[\"finalwgt\"],\n    x=nhanes2brr[\"height\"],\n    rep_weights=nhanes2brr.loc[:, \"brr_1\":\"brr_32\"],\n    remove_nan=True,\n)\n\nprint(ratio_wgt_hgt)\n\nSAMPLICS - Estimation of Ratio\n\nNumber of strata: None\nNumber of psus: None\nDegree of freedom: 16\n\n   RATIO      SE      LCI     UCI       CV\n0.426082 0.00273 0.420295 0.43187 0.006407\n\n\n\n\nJackknife\n\n# Load NMIHS sample data\nnhanes2jk_dict = load_nhanes2jk()\nnhanes2jk = nhanes2jk_dict[\"data\"]\n\nnhanes2jk.head(15)\n\n\n\n\n\n\n\n\nheight\nweight\nfinalwgt\njkw_1\njkw_2\njkw_3\njkw_4\njkw_5\njkw_6\njkw_7\n...\njkw_53\njkw_54\njkw_55\njkw_56\njkw_57\njkw_58\njkw_59\njkw_60\njkw_61\njkw_62\n\n\n\n\n0\n174.59801\n62.480000\n8995\n0\n17990\n8995\n8995\n8995\n8995\n8995\n...\n8995\n8995\n8995\n8995\n8995\n8995\n8995\n8995\n8995\n8995\n\n\n1\n152.29700\n48.759998\n25964\n0\n51928\n25964\n25964\n25964\n25964\n25964\n...\n25964\n25964\n25964\n25964\n25964\n25964\n25964\n25964\n25964\n25964\n\n\n2\n164.09801\n67.250000\n8752\n0\n17504\n8752\n8752\n8752\n8752\n8752\n...\n8752\n8752\n8752\n8752\n8752\n8752\n8752\n8752\n8752\n8752\n\n\n3\n162.59801\n94.459999\n4310\n0\n8620\n4310\n4310\n4310\n4310\n4310\n...\n4310\n4310\n4310\n4310\n4310\n4310\n4310\n4310\n4310\n4310\n\n\n4\n163.09801\n74.279999\n9011\n0\n18022\n9011\n9011\n9011\n9011\n9011\n...\n9011\n9011\n9011\n9011\n9011\n9011\n9011\n9011\n9011\n9011\n\n\n5\n147.09801\n66.000000\n4310\n0\n8620\n4310\n4310\n4310\n4310\n4310\n...\n4310\n4310\n4310\n4310\n4310\n4310\n4310\n4310\n4310\n4310\n\n\n6\n153.89799\n54.549999\n3201\n0\n6402\n3201\n3201\n3201\n3201\n3201\n...\n3201\n3201\n3201\n3201\n3201\n3201\n3201\n3201\n3201\n3201\n\n\n7\n160.00000\n58.970001\n25386\n0\n50772\n25386\n25386\n25386\n25386\n25386\n...\n25386\n25386\n25386\n25386\n25386\n25386\n25386\n25386\n25386\n25386\n\n\n8\n164.00000\n68.949997\n12102\n0\n24204\n12102\n12102\n12102\n12102\n12102\n...\n12102\n12102\n12102\n12102\n12102\n12102\n12102\n12102\n12102\n12102\n\n\n9\n176.59801\n65.430000\n4312\n0\n8624\n4312\n4312\n4312\n4312\n4312\n...\n4312\n4312\n4312\n4312\n4312\n4312\n4312\n4312\n4312\n4312\n\n\n10\n156.19901\n76.769997\n4031\n0\n8062\n4031\n4031\n4031\n4031\n4031\n...\n4031\n4031\n4031\n4031\n4031\n4031\n4031\n4031\n4031\n4031\n\n\n11\n156.69901\n57.040001\n3813\n7626\n0\n3813\n3813\n3813\n3813\n3813\n...\n3813\n3813\n3813\n3813\n3813\n3813\n3813\n3813\n3813\n3813\n\n\n12\n182.00000\n99.110001\n7445\n14890\n0\n7445\n7445\n7445\n7445\n7445\n...\n7445\n7445\n7445\n7445\n7445\n7445\n7445\n7445\n7445\n7445\n\n\n13\n158.29700\n84.480003\n3528\n7056\n0\n3528\n3528\n3528\n3528\n3528\n...\n3528\n3528\n3528\n3528\n3528\n3528\n3528\n3528\n3528\n3528\n\n\n14\n152.89799\n70.309998\n7790\n15580\n0\n7790\n7790\n7790\n7790\n7790\n...\n7790\n7790\n7790\n7790\n7790\n7790\n7790\n7790\n7790\n7790\n\n\n\n\n15 rows × 65 columns\n\n\n\nIn this case, stratification was used to calculate the jackknife weights. The stratum variable is not indicated in the dataset or survey design description. However, it says that the number of strata is 31 and the number of replicates is 62. Hence, the jackknife replicate coefficient is \\((n_h - 1) / n_h = (2-1) / 2 = 0.5\\). Now we can call replicate() and specify rep_coefs = 0.5.\n\njackknife = ReplicateEstimator(RepMethod.jackknife, PopParam.ratio)\n\nratio_wgt_hgt2 = jackknife.estimate(\n    y=nhanes2jk[\"weight\"],\n    samp_weight=nhanes2jk[\"finalwgt\"],\n    x=nhanes2jk[\"height\"],\n    rep_weights=nhanes2jk.loc[:, \"jkw_1\":\"jkw_62\"],\n    rep_coefs=0.5,\n    remove_nan=True,\n)\n\nprint(ratio_wgt_hgt2)\n\nSAMPLICS - Estimation of Ratio\n\nNumber of strata: None\nNumber of psus: None\nDegree of freedom: 61\n\n   RATIO       SE      LCI      UCI      CV\n0.423502 0.003464 0.416574 0.430429 0.00818",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Population Parameters Estimation",
      "Replicated-based Estimation"
    ]
  },
  {
    "objectID": "pages/datasets.html",
    "href": "pages/datasets.html",
    "title": "Datasets",
    "section": "",
    "text": "The module datasets allows the user to load the datasets used in this tutorial. Note that the datasets are only used to illustrate the syntax and APIs of Samplics. Many of the datasets in this tutorial are subsets of actual samples but DO NOT represent these samples. The datasets were subseted from existing samples to reduce the size of the files.\n\n\n\n\n\n\nTip\n\n\n\na dataset can be loaded using the function load_xxx() where xxx indicates the dataset name.\nFor example, load_psu_frame() loads the PSU frame dataset.\n\n\nThese functions return a dictionary with the following members: name, description, nrows, ncols, design, source, and, data. The current list of datasets is the following:\n\nBirth: This dataset represent a city data of categories of birth by age group. The dataset was obtained through the public stata API. Use load_birth() to load the dataset.\nCountyCrop and CountryCropMeans: These datasets were used by Battese, Harter, and Fuller (1988) for their pioner paper on small area estimation. Use load_county_crop() and load_county_crop_means() to load the datasets.\nExpenditureMilk: The Milk Expenditure data contains 43 observations on the average expenditure on fresh milk for the year 1989. This dataset was originally used by Arora and Lahiri (1997) and later by You and Chapman (2006). Use load_expenditure_milk() to load the dataset.\nNhanes2, Nhanes2brr, and Nhanes2jk: these datasets were obtained from the NHANES (McDowell et al. 1981)_. As mentioned above, the datasets are only subsets of the full sample and do not represent the NHANES II study. This data is only useful for illustrating the syntax of samplics. These datasets should not be used to conduct any analysis of NHANES nor use the numbers for any statistical analysis. The original data was obtained through the public stata API. Use load_nhanes2(), load_nhanes2brr(), and load_nhanes2jk() to load the datasets.\nNmihs: The dataset is a subset of the National Maternal and Infant Health Survey (NMIHS) sample (Gonzalez Jr, N, and C 1992). The dataset should not be used to conduct any analysis of NMIHS nor use the numbers for any statistical analysis. The original data was obtained through the public stata API. Use load_nmihs() to load the dataset.\nPSUFrame, PSUSample, and SSUSample: these are simulated datasets to illustrate the selection of primary and secondary sampling units. Use load_psu_frame(), load_psu_sample(), and load_ssu_sample() to load the datasets.\n\nLet’s assume we want to load the PSU frame, we could write the following code.\n\nimport samplics\n\n# Import the appropriate class.\nfrom samplics.datasets import load_psu_frame\n\n# Load the dataset and its metadata into \n# the variable (dictionary) psu_frame_dict\npsu_frame_dict = load_psu_frame()\n\n# Store the datasets in the variable psu_frame (optional)\npsu_frame = psu_frame_dict[\"data\"]\n\n\n\n\n\n\n\nImportant\n\n\n\nThe datasets should not be used for any statistical analysis.  No number shown in this tutorial shall be used for any statistical analysis.  All the examples are exclusively for illustrating the syntax and APIs of Samplics.\n\n\n\n\n\n\nReferences\n\nBattese, G E, R M Harter, and W A Fuller. 1988. “An Error-Components Model for Prediction of County Crop Areas Using Survey and Satellite Data.” J. Amer. Statist. Assoc. 83: 28–36.\n\n\nGonzalez Jr, J F, Krauss N, and Scott C. 1992. “Estimation in the 1988 National Maternal and Infant Health Survey.” In Proceedings of the Section on Statistics Education, edited by American Statistical Association, 343–48. https://doi.org/ 10.25080/Majora-92bf1922-00a .\n\n\nMcDowell, A, A Engel, J T Massey, and K Maurer. 1981. “Lan and Operation of the Second National Health and Nutrition Examination Survey, 1976–1980.” Vital and Health Statistics 1 (15): 1–144.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Datasets"
    ]
  },
  {
    "objectID": "pages/weight_adj.html",
    "href": "pages/weight_adj.html",
    "title": "Sample Weight Adjustments",
    "section": "",
    "text": "The objective of this tutorial is to familiarize ourselves with SampleWeight the samplics class for adjusting sample weights. In practice, it is necessary to adjust base or design sample weights obtained directly from the random sample mechanism. These adjustments are done to correct for nonresponse, reduce effects of extreme/large weights, better align with known auxiliary information, and more. Specifically in this tutorial, we will:\nTo run the code in this notebook, we will use the dataset that was developed in the previous tutorial on sample selection.\nimport numpy as np\nimport pandas as pd\n\nfrom samplics.datasets import load_psu_sample, load_ssu_sample\nfrom samplics.weighting import SampleWeight",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Weighting",
      "Sample Weight Adjustments"
    ]
  },
  {
    "objectID": "pages/weight_adj.html#nonresponse-adjustment",
    "href": "pages/weight_adj.html#nonresponse-adjustment",
    "title": "Sample Weight Adjustments",
    "section": "Nonresponse adjustment",
    "text": "Nonresponse adjustment\nIn general, the sample weights are adjusted to redistribute the sample weights of all eligible units for which there is no sufficient response (unit level nonresponse) to the sampling units that sufficiently responded to the survey. This adjustment is done within adjustment classes or domains. Note that the determination of the response categories (unit response, item response, ineligible, etc.) is outside of the scope of this tutorial.\nAlso, the weights of the sampling units with unknown eligibility are redistributed to the rest of the sampling units. In general, ineligible sampling units receive weights from the sampling units with unknown eligibility since eligible sampling units can be part of the unknown pool.\nThe method adjust() has a boolean parameter unknown_to_inelig which controls how the sample weights of the unknown is redistributed. By default, adjust() redistribute the sample weights of the sampling units of the unknown to the ineligibles (unknown_to_inelig=True). If we do not wish to redistribute the sample weights of the unknowns to the ineligibles then we just set the flag to False (unknown_to_inelig=Fasle).\nIn the snippet of code below, we adjust the weight within clusters that is we use clusters as our adjustment classes. Note that we run the nonresponse adjustment twice, the first time with unknown_to_inelig=True (nr_weight) and the second time with the flag equal to False (nr_weight2). With unknown_to_inelig=True, the ineligible received part of the sample weights from the unknowns. Hence, the sample weights for the respondent is less than when the flag is False. With unknown_to_inelig=Fasle, the ineligible did Not receive any weights from the unknowns. Hence, the sample weights for the ineligible units remain the same before and after adjustment. In a real survey, the statistician may decide on the best non-response strategy based on the available information.\n\nstatus_mapping = {\n    \"in\": \"ineligible\", \n    \"rr\": \"respondent\", \n    \"nr\": \"non-respondent\", \n    \"uk\": \"unknown\"\n    }\n\nfull_sample[\"nr_weight\"] = SampleWeight().adjust(\n    samp_weight=full_sample[\"design_weight\"],\n    adj_class=full_sample[[\"region\", \"cluster\"]],\n    resp_status=full_sample[\"response_status\"],\n    resp_dict=status_mapping,\n)\n\nfull_sample[\"nr_weight2\"] = SampleWeight().adjust(\n    samp_weight=full_sample[\"design_weight\"],\n    adj_class=full_sample[[\"region\", \"cluster\"]],\n    resp_status=full_sample[\"response_status\"],\n    resp_dict=status_mapping,\n    unknown_to_inelig=False,\n)\n\nfull_sample[[\n    \"cluster\", \n    \"region\", \n    \"design_weight\", \n    \"response_status\", \n    \"nr_weight\", \n    \"nr_weight2\"\n    ]].drop_duplicates().head(15)\n\n\n\n\n\n\n\n\ncluster\nregion\ndesign_weight\nresponse_status\nnr_weight\nnr_weight2\n\n\n\n\n0\n7\nNorth\n46.166667\nineligible\n49.464286\n46.166667\n\n\n1\n7\nNorth\n46.166667\nrespondent\n54.410714\n55.400000\n\n\n4\n7\nNorth\n46.166667\nunknown\n0.000000\n0.000000\n\n\n11\n7\nNorth\n46.166667\nnon-respondent\n0.000000\n0.000000\n\n\n15\n10\nNorth\n50.783333\nnon-respondent\n0.000000\n0.000000\n\n\n16\n10\nNorth\n50.783333\nrespondent\n70.733929\n71.096667\n\n\n19\n10\nNorth\n50.783333\nineligible\n54.410714\n50.783333\n\n\n21\n10\nNorth\n50.783333\nunknown\n0.000000\n0.000000\n\n\n30\n16\nSouth\n62.149123\nrespondent\n66.588346\n66.588346\n\n\n35\n16\nSouth\n62.149123\nnon-respondent\n0.000000\n0.000000\n\n\n45\n24\nSouth\n58.940741\nrespondent\n63.852469\n63.852469\n\n\n47\n24\nSouth\n58.940741\nnon-respondent\n0.000000\n0.000000\n\n\n55\n24\nSouth\n58.940741\nineligible\n58.940741\n58.940741\n\n\n60\n29\nSouth\n65.702778\nunknown\n0.000000\n0.000000\n\n\n61\n29\nSouth\n65.702778\nrespondent\n101.081197\n102.204321\n\n\n\n\n\n\n\n\n\n\n\n\n\nImportant\n\n\n\nThe default call of adjust() expects standard codes for response status that is “in”, “rr”, “nr”, and “uk” where “in” means ineligible, “rr” means respondent, “nr” means non-respondent, and “uk” means unknown eligibility.\n\n\nIn the call above, if we omit the parameter response_dict, then the run would fail with an assertion error message. The current error message is the following: The response status must only contains values in (‘in’, ‘rr’, ‘nr’, ‘uk’) or the mapping should be provided using response_dict parameter. For the call to run without using response_dict, it is necessary that the response status takes only values in the standard codes i.e. (“in”, “rr”, “nr”, “uk”). The variable associated with response_status can contain any code but a mapping is necessary when the response variable is not constructed using the standard codes.\nTo further illustrate the mapping of response status, let’s assume that we have response_status2 which has the values 100 for ineligible, 200 for non-respondent, 300 for respondent, and 999 for unknown.\n\nresponse_status2 = np.repeat(100, full_sample[\"response_status\"].shape[0])\nresponse_status2[full_sample[\"response_status\"] == \"non-respondent\"] = 200\nresponse_status2[full_sample[\"response_status\"] == \"respondent\"] = 300\nresponse_status2[full_sample[\"response_status\"] == \"unknown\"] = 999\n\npd.crosstab(response_status2, full_sample[\"response_status\"])\n\n\n\n\n\n\n\nresponse_status\nineligible\nnon-respondent\nrespondent\nunknown\n\n\nrow_0\n\n\n\n\n\n\n\n\n100\n16\n0\n0\n0\n\n\n200\n0\n23\n0\n0\n\n\n300\n0\n0\n106\n0\n\n\n999\n0\n0\n0\n5\n\n\n\n\n\n\n\nTo use response_status2, we need to map the values 100, 200, 300 and 999 to “in”, “rr”, “nr”, and “uk”. This mapping is done below using the Python dictionary status_mapping2. Using status_mapping2 in the function call adjust() will lead to the same adjustment as in the previous run i.e. nr_weight and nr_weight3 contain the same adjusted weights.\n\nstatus_mapping2 = {\"in\": 100, \"nr\": 200, \"rr\": 300, \"uk\": 999}\n\nfull_sample[\"nr_weight3\"] = SampleWeight().adjust(\n    samp_weight=full_sample[\"design_weight\"],\n    adj_class=full_sample[[\"region\", \"cluster\"]],\n    resp_status=response_status2,\n    resp_dict=status_mapping2,\n)\n\nfull_sample[\n    [\"cluster\", \"region\", \"response_status\", \"nr_weight\", \"nr_weight3\"]\n    ].drop_duplicates().head()\n\n\n\n\n\n\n\n\ncluster\nregion\nresponse_status\nnr_weight\nnr_weight3\n\n\n\n\n0\n7\nNorth\nineligible\n49.464286\n49.464286\n\n\n1\n7\nNorth\nrespondent\n54.410714\n54.410714\n\n\n4\n7\nNorth\nunknown\n0.000000\n0.000000\n\n\n11\n7\nNorth\nnon-respondent\n0.000000\n0.000000\n\n\n15\n10\nNorth\nnon-respondent\n0.000000\n0.000000\n\n\n\n\n\n\n\nIf the response status variable only takes values “in”, “nr”, “rr” and “uk”, then it is not necessary to provide the mapping dictionary to the function i.e. resp_dict can be omitted from the function call adjust().\n\nresponse_status3 = np.repeat(\"in\", full_sample[\"response_status\"].shape[0])\nresponse_status3[full_sample[\"response_status\"] == \"non-respondent\"] = \"nr\"\nresponse_status3[full_sample[\"response_status\"] == \"respondent\"] = \"rr\"\nresponse_status3[full_sample[\"response_status\"] == \"unknown\"] = \"uk\"\n\nfull_sample[\"nr_weight4\"] = SampleWeight().adjust(\n    samp_weight=full_sample[\"design_weight\"],\n    adj_class=full_sample[[\"region\", \"cluster\"]],\n    resp_status=response_status3,\n)\n\nfull_sample[\n    [\"cluster\", \"region\", \"response_status\", \"nr_weight\", \"nr_weight4\"]\n    ].drop_duplicates().head()\n\n\n\n\n\n\n\n\ncluster\nregion\nresponse_status\nnr_weight\nnr_weight4\n\n\n\n\n0\n7\nNorth\nineligible\n49.464286\n49.464286\n\n\n1\n7\nNorth\nrespondent\n54.410714\n54.410714\n\n\n4\n7\nNorth\nunknown\n0.000000\n0.000000\n\n\n11\n7\nNorth\nnon-respondent\n0.000000\n0.000000\n\n\n15\n10\nNorth\nnon-respondent\n0.000000\n0.000000\n\n\n\n\n\n\n\n\n# Just dropping a couple of variables \n# not needed for the rest of the tutorial\nfull_sample.drop(\n    columns=[\n        \"psu_prob\", \n        \"ssu_prob\", \n        \"inclusion_prob\", \n        \"nr_weight2\", \n        \"nr_weight3\", \n        \"nr_weight4\"\n        ], \n    inplace=True\n)",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Weighting",
      "Sample Weight Adjustments"
    ]
  },
  {
    "objectID": "pages/weight_adj.html#poststratification",
    "href": "pages/weight_adj.html#poststratification",
    "title": "Sample Weight Adjustments",
    "section": "Poststratification",
    "text": "Poststratification\nPoststratification is useful to compensate for under-representation of the sample or to correct for nonsampling error. The most common poststratification method consists of adjusting the sample weights to ensure that they sum to known control values from reliable souces by adjustment classes (domains). Poststratification classes can be formed using variables beyond the ones involved in the sampling design. For example, socio-economic variables such as age group, gender, race and education are often used to form poststratification classes/cells.\n\n\n\n\n\n\nWarning\n\n\n\npoststratifying to totals that are known to be out of date, and thus likely inaccurate and/or unreliable may not improve the estimate. Use this with caution.\n\n\nLet’s assume that we have a reliable external source e.g. a recent census that provides the number of households by region. The external source has the following control data: 3700 households for East, 1500 for North, 2800 for South and 6500 for West.\nWe use the method poststratify() to ensure that the poststratified sample weights (ps_weight) sum to the know control totals by region. Note that the control totals are provided using the Python dictionary census_households.\n\ncensus_households = {\"East\": 3700, \"North\": 1500, \"South\": 2800, \"West\": 6500}\n\nfull_sample[\"ps_weight\"] = SampleWeight().poststratify(\n    samp_weight=full_sample[\"nr_weight\"], \n    control=census_households, \n    domain=full_sample[\"region\"]\n)\n\nfull_sample.head(15)\n\n\n\n\n\n\n\n\ncluster\nregion\nhousehold\ndesign_weight\nresponse_status\nnr_weight\nps_weight\n\n\n\n\n0\n7\nNorth\n72\n46.166667\nineligible\n49.464286\n51.020408\n\n\n1\n7\nNorth\n73\n46.166667\nrespondent\n54.410714\n56.122449\n\n\n2\n7\nNorth\n75\n46.166667\nrespondent\n54.410714\n56.122449\n\n\n3\n7\nNorth\n715\n46.166667\nrespondent\n54.410714\n56.122449\n\n\n4\n7\nNorth\n722\n46.166667\nunknown\n0.000000\n0.000000\n\n\n5\n7\nNorth\n724\n46.166667\nrespondent\n54.410714\n56.122449\n\n\n6\n7\nNorth\n755\n46.166667\nrespondent\n54.410714\n56.122449\n\n\n7\n7\nNorth\n761\n46.166667\nineligible\n49.464286\n51.020408\n\n\n8\n7\nNorth\n764\n46.166667\nrespondent\n54.410714\n56.122449\n\n\n9\n7\nNorth\n782\n46.166667\nrespondent\n54.410714\n56.122449\n\n\n10\n7\nNorth\n795\n46.166667\nrespondent\n54.410714\n56.122449\n\n\n11\n7\nNorth\n7111\n46.166667\nnon-respondent\n0.000000\n0.000000\n\n\n12\n7\nNorth\n7112\n46.166667\nrespondent\n54.410714\n56.122449\n\n\n13\n7\nNorth\n7117\n46.166667\nineligible\n49.464286\n51.020408\n\n\n14\n7\nNorth\n7123\n46.166667\nrespondent\n54.410714\n56.122449\n\n\n\n\n\n\n\nThe snippet of code below shows that the poststratified sample weights sum to the expected control totals that is 3700 households for East, 1500 for North, 2800 for South and 6500 for West.\n\nsum_of_weights = full_sample[\n    [\"region\", \"nr_weight\", \"ps_weight\"]\n    ].groupby(\"region\").sum()\nsum_of_weights.reset_index(inplace=True)\nsum_of_weights.head()\n\n\n\n\n\n\n\n\nregion\nnr_weight\nps_weight\n\n\n\n\n0\nEast\n3698.703391\n3700.0\n\n\n1\nNorth\n1454.250000\n1500.0\n\n\n2\nSouth\n2801.889620\n2800.0\n\n\n3\nWest\n6485.783333\n6500.0\n\n\n\n\n\n\n\nThe crosstable below shows that only one adjustment factor was calculated and applied per adjustment class or region.\n\nfull_sample[\"ps_adj_fct\"] = \\\n    round(full_sample[\"ps_weight\"] / full_sample[\"nr_weight\"], 12)\n\npd.crosstab(full_sample[\"ps_adj_fct\"], full_sample[\"region\"])\n\n\n\n\n\n\n\nregion\nEast\nNorth\nSouth\nWest\n\n\nps_adj_fct\n\n\n\n\n\n\n\n\n0.999326\n0\n0\n38\n0\n\n\n1.000351\n37\n0\n0\n0\n\n\n1.002192\n0\n0\n0\n23\n\n\n1.031460\n0\n24\n0\n0\n\n\n\n\n\n\n\nIn some surveys, there is interest in keeping relative distribution of strata to some known distribution. For example, WHO EPI vaccination surveys often postratify sample weights to ensure that relative sizes of strata reflect offcial statistics e.g. census data. In most cases, the strata are based on some administrative divisions.\nFor example, assume that according to census data that East contains 25% of the households, North contains 10%, South contains 20% and West contains 45%. We can poststratify using the snippet of code below.\n\nknown_ratios = {\"East\": 0.25, \"North\": 0.10, \"South\": 0.20, \"West\": 0.45}\nfull_sample[\"ps_weight2\"] = SampleWeight().poststratify(\n    samp_weight=full_sample[\"nr_weight\"], \n    factor=known_ratios, \n    domain=full_sample[\"region\"]\n)\n\nfull_sample.head()\n\n\n\n\n\n\n\n\ncluster\nregion\nhousehold\ndesign_weight\nresponse_status\nnr_weight\nps_weight\nps_adj_fct\nps_weight2\n\n\n\n\n0\n7\nNorth\n72\n46.166667\nineligible\n49.464286\n51.020408\n1.03146\n49.117777\n\n\n1\n7\nNorth\n73\n46.166667\nrespondent\n54.410714\n56.122449\n1.03146\n54.029554\n\n\n2\n7\nNorth\n75\n46.166667\nrespondent\n54.410714\n56.122449\n1.03146\n54.029554\n\n\n3\n7\nNorth\n715\n46.166667\nrespondent\n54.410714\n56.122449\n1.03146\n54.029554\n\n\n4\n7\nNorth\n722\n46.166667\nunknown\n0.000000\n0.000000\nNaN\n0.000000\n\n\n\n\n\n\n\n\nsum_of_weights2 = full_sample[\n    [\"region\", \"nr_weight\", \"ps_weight2\"]\n    ].groupby(\"region\").sum()\nsum_of_weights2.reset_index(inplace=True)\nsum_of_weights2[\"ratio\"] = \\\n    sum_of_weights2[\"ps_weight2\"] / sum(sum_of_weights2[\"ps_weight2\"])\nsum_of_weights2.head()\n\n\n\n\n\n\n\n\nregion\nnr_weight\nps_weight2\nratio\n\n\n\n\n0\nEast\n3698.703391\n3610.156586\n0.25\n\n\n1\nNorth\n1454.250000\n1444.062634\n0.10\n\n\n2\nSouth\n2801.889620\n2888.125269\n0.20\n\n\n3\nWest\n6485.783333\n6498.281855\n0.45\n\n\n\n\n\n\n\n\nCalibration\nCalibration is a more general concept for adjusting sample weights to sum to known constants. In this tutorial, we consider the generalized regression (GREG) class of calibration. Assume that we have \\(\\hat{\\mathbf{Y}} = \\sum_{i \\in s} w_i y_i\\) and know population totals \\(\\mathbf{X} = (\\mathbf{X}_1, ..., \\mathbf{X}_p)^T\\) are available. Working under the model \\(Y_i | \\mathbf{x}_i = \\mathbf{x}^T_i \\mathbf{\\beta} + \\epsilon_i\\), the GREG estimator of the population total is\n\\[\\hat{\\mathbf{Y}}_{GR} = \\hat{\\mathbf{Y}} + (\\mathbf{X} - \\hat{\\mathbf{X}})^T\\hat{\\mathbf{B}}\\]\nwhere \\(\\hat{\\mathbf{B}}\\) is the weighted least squares estimate of \\(\\mathbf{\\beta}\\) and \\(\\hat{\\mathbf{X}}\\) is the survey estimate of \\(\\mathbf{X}\\). The essential of the GREG approach is, under the regression model, to find the adjusted weights \\(w^{*}_i\\) that are the closest to \\(w_i\\), to minimize \\(h(z) = \\frac{\\sum_{i \\in s} c_i(w_i - z_i)}{w_i}\\).\nLet us simulate three auxiliary variables that is education, poverty and under_five (number of children under five in the household) and assume that we have the following control totals.\n\nTotal number of under five children: 6300 in the East, 4000 in the North, 6500 in the South and 14000 in the West.\nPoverty (Yes: in poverty / No: not in poverty)\n\n\n\nRegion  \nPoverty  \nNumber of households\n\n\n\n\nEast\nNo\n2600\n\n\n\nYes\n1200\n\n\nNorth\nNo\n1500\n\n\n\nYes\n200\n\n\nSouth\nNo\n1800\n\n\n\nYes\n1100\n\n\nWest\nNo\n4500\n\n\n\nYes\n2200\n\n\n\nEducation (Low: less than secondary, Medium: secondary completed, and High: More than secondary)\n\n\n\nRegion  \nEducation  \nNumber of households\n\n\n\n\nEast\nLow\n2000\n\n\n\nMedium\n1400\n\n\n\nHigh\n350\n\n\nNorth\nLow\n550\n\n\n\nMedium\n700\n\n\n\nHigh\n250\n\n\nSouth\nLow\n1300\n\n\n\nMedium\n1200\n\n\n\nHigh\n350\n\n\nWest\nLow\n2100\n\n\n\nMedium\n4000\n\n\n\nHigh\n500\n\n\n\n\n\nnp.random.seed(150)\nfull_sample[\"education\"] = np.random.choice(\n    (\"Low\", \"Medium\", \"High\"), \n    size=150, \n    p=(0.40, 0.50, 0.10)\n    )\nfull_sample[\"poverty\"] = np.random.choice((0, 1), size=150, p=(0.70, 0.30))\nfull_sample[\"under_five\"] = np.random.choice(\n    (0, 1, 2, 3, 4, 5), \n    size=150, \n    p=(0.05, 0.35, 0.25, 0.20, 0.10, 0.05)\n    )\n\nfull_sample[[\n    \"cluster\", \n    \"region\", \n    \"household\", \n    \"nr_weight\", \n    \"education\", \n    \"poverty\", \n    \"under_five\"\n    ]].head()\n\n\n\n\n\n\n\n\ncluster\nregion\nhousehold\nnr_weight\neducation\npoverty\nunder_five\n\n\n\n\n0\n7\nNorth\n72\n49.464286\nHigh\n1\n1\n\n\n1\n7\nNorth\n73\n54.410714\nLow\n0\n3\n\n\n2\n7\nNorth\n75\n54.410714\nMedium\n0\n2\n\n\n3\n7\nNorth\n715\n54.410714\nMedium\n1\n2\n\n\n4\n7\nNorth\n722\n0.000000\nMedium\n0\n2\n\n\n\n\n\n\n\nWe now will calibrate the nonreponse weight (nr_weight) to ensure that the estimated number of households in poverty is equal to 4,700 and the estimated total number of children under five is 30,8500. The control numbers 4,700 and 30,800 are obtained from the table above.\nThe class SampleWeight() uses the method calibrate(samp_weight, aux_vars, control, domain, scale, bounded, modified) to adjust the weight using the GREG approach. * The contol values must be stored in a python dictionnary i.e. totals = {“poverty”: 4700, “under_five”: 30800}. In this case, we have two numerical variables poverty with values in {0, 1} and under_five with values in {0, 1, 2, 3, 4, 5}. * aux_vars is the matrix of covariates.\n\ntotals = {\"poverty\": 4700, \"under_five\": 30800}\n\nfull_sample[\"calib_weight\"] = SampleWeight().calibrate(\n    full_sample[\"nr_weight\"], full_sample[[\"poverty\", \"under_five\"]], totals\n)\n\nfull_sample[[\"cluster\", \"region\", \"household\", \"nr_weight\", \"calib_weight\"]].head(15)\n\n\n\n\n\n\n\n\ncluster\nregion\nhousehold\nnr_weight\ncalib_weight\n\n\n\n\n0\n7\nNorth\n72\n49.464286\n50.432441\n\n\n1\n7\nNorth\n73\n54.410714\n57.233887\n\n\n2\n7\nNorth\n75\n54.410714\n56.292829\n\n\n3\n7\nNorth\n715\n54.410714\n56.416743\n\n\n4\n7\nNorth\n722\n0.000000\n0.000000\n\n\n5\n7\nNorth\n724\n54.410714\n57.233887\n\n\n6\n7\nNorth\n755\n54.410714\n57.233887\n\n\n7\n7\nNorth\n761\n49.464286\n49.464286\n\n\n8\n7\nNorth\n764\n54.410714\n56.292829\n\n\n9\n7\nNorth\n782\n54.410714\n57.233887\n\n\n10\n7\nNorth\n795\n54.410714\n58.174944\n\n\n11\n7\nNorth\n7111\n0.000000\n0.000000\n\n\n12\n7\nNorth\n7112\n54.410714\n59.116002\n\n\n13\n7\nNorth\n7117\n49.464286\n50.319793\n\n\n14\n7\nNorth\n7123\n54.410714\n56.292829\n\n\n\n\n\n\n\nWe can confirm that the estimated totals for the auxiliary variables are equal to their control values.\n\npoverty = full_sample[\"poverty\"]\nunder_5 = full_sample[\"under_five\"]\nnr_weight = full_sample[\"nr_weight\"]\ncalib_weight = full_sample[\"calib_weight\"]\n\nprint(\n    f\"\"\"\\nTotal estimated number of poor households was \n    {sum(poverty*nr_weight):.2f} before adjustment and \n    {sum(poverty*calib_weight):.2f} after adjustment.\\n\"\"\"\n)\nprint(\n    f\"\"\"Total estimated number of children under 5 was \n    {sum(under_5*nr_weight):.2f} before adjustment and \n    {sum(under_5*calib_weight):.2f} after adjustment.\\n\"\"\"\n)\n\n\nTotal estimated number of poor households was \n    4521.84 before adjustment and \n    4700.00 after adjustment.\n\nTotal estimated number of children under 5 was \n    29442.52 before adjustment and \n    30800.00 after adjustment.\n\n\n\nIf we want to control by domain then we can do so using the parameter domain of calibrate(). First we need to update the python dictionary holding the control values. Now, those values have to be provided for each domain. Note that the dictionary is now a nested dictionary where the higher level keys hold the domain values i.e. East, North, South and West. Then the higher level values of the dictionary are the dictionaries providing mapping for the auxiliary variables and the corresponding control values.\n\ntotals_by_domain = {\n    \"East\": {\"poverty\": 1200, \"under_five\": 6300},\n    \"North\": {\"poverty\": 200, \"under_five\": 4000},\n    \"South\": {\"poverty\": 1100, \"under_five\": 6500},\n    \"West\": {\"poverty\": 2200, \"under_five\": 14000},\n}\n\nfull_sample[\"calib_weight_d\"] = SampleWeight().calibrate(\n    full_sample[\"nr_weight\"], \n    full_sample[[\"poverty\", \"under_five\"]], \n    totals_by_domain, full_sample[\"region\"]\n)\n\nfull_sample[[\n    \"cluster\", \n    \"region\", \n    \"household\", \n    \"nr_weight\", \n    \"calib_weight\", \n    \"calib_weight_d\"\n    ]].head(15)\n\n\n\n\n\n\n\n\ncluster\nregion\nhousehold\nnr_weight\ncalib_weight\ncalib_weight_d\n\n\n\n\n0\n7\nNorth\n72\n49.464286\n50.432441\n40.892864\n\n\n1\n7\nNorth\n73\n54.410714\n57.233887\n61.852139\n\n\n2\n7\nNorth\n75\n54.410714\n56.292829\n59.371664\n\n\n3\n7\nNorth\n715\n54.410714\n56.416743\n47.462625\n\n\n4\n7\nNorth\n722\n0.000000\n0.000000\n0.000000\n\n\n5\n7\nNorth\n724\n54.410714\n57.233887\n61.852139\n\n\n6\n7\nNorth\n755\n54.410714\n57.233887\n61.852139\n\n\n7\n7\nNorth\n761\n49.464286\n49.464286\n49.464286\n\n\n8\n7\nNorth\n764\n54.410714\n56.292829\n59.371664\n\n\n9\n7\nNorth\n782\n54.410714\n57.233887\n61.852139\n\n\n10\n7\nNorth\n795\n54.410714\n58.174944\n64.332614\n\n\n11\n7\nNorth\n7111\n0.000000\n0.000000\n0.000000\n\n\n12\n7\nNorth\n7112\n54.410714\n59.116002\n66.813089\n\n\n13\n7\nNorth\n7117\n49.464286\n50.319793\n51.719263\n\n\n14\n7\nNorth\n7123\n54.410714\n56.292829\n59.371664\n\n\n\n\n\n\n\nNote that the GREG domain estimates above do not have the additive property. That is the GREG domain estimates do not sum to the overal GREG estimate. To illustrate this, let’s assume that we want to estimate the number of households.\n\nprint(f\"\\nThe number of households using the overall GREG is: \\\n    {sum(full_sample['calib_weight']):.2f} \\n\")\nprint(f\"The number of households using the domain GREG is: \\\n    {sum(full_sample['calib_weight_d']):.2f} \\n\")\n\n\nThe number of households using the overall GREG is:     14960.15 \n\nThe number of households using the domain GREG is:     14959.01 \n\n\n\n\n\n\n\n\n\nNote\n\n\n\nIf the additive flag is set to True, the sum of the domain estimates will be equal to the GREG overal estimate.\n\n\n\ntotals_by_domain = {\n    \"East\": {\"poverty\": 1200, \"under_five\": 6300},\n    \"North\": {\"poverty\": 200, \"under_five\": 4000},\n    \"South\": {\"poverty\": 1100, \"under_five\": 6500},\n    \"West\": {\"poverty\": 2200, \"under_five\": 14000},\n}\n\ncalib_weight3 = SampleWeight().calibrate(\n    full_sample[\"nr_weight\"],\n    full_sample[[\"poverty\", \"under_five\"]],\n    totals_by_domain,\n    full_sample[\"region\"],\n    additive=True,\n)\n\n\nunder_5 = np.array(full_sample[\"under_five\"])\nprint(f\"\\nEach column can be used to estimate a domain: \\\n{np.sum(np.transpose(calib_weight3) * under_5, axis=1)}\\n\")\n\n\nEach column can be used to estimate a domain: [ 6300.  4000.  6500. 14000.]\n\n\n\n\nprint(f\"The number of households using the overall GREG is: \\\n{sum(full_sample['calib_weight']):.2f} \\n\")\n\nThe number of households using the overall GREG is: 14960.15 \n\n\n\n\nprint(f\"The number of households using the domain GREG is: \\\n{sum(full_sample['calib_weight_d']):.2f} (additive=False)\\n\")\n\nThe number of households using the domain GREG is: 14959.01 (additive=False)\n\n\n\n\nprint(f\"The number of households using the domain GREG is: \\\n{np.sum(np.transpose(calib_weight3)):.2f} (additive=True) \\n\")\n\nThe number of households using the domain GREG is: 14960.15 (additive=True)",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Weighting",
      "Sample Weight Adjustments"
    ]
  },
  {
    "objectID": "pages/weight_adj.html#normalization",
    "href": "pages/weight_adj.html#normalization",
    "title": "Sample Weight Adjustments",
    "section": "Normalization",
    "text": "Normalization\nDHS and MICS normalize the final sample weights to sum to the sample size. We can use the class method normalize() to ensure that the sample weight sum to some constant across the sample or by normalization domain e.g. stratum.\n\n\n\n\n\n\nNote\n\n\n\nnormalization is mostly added here for completeness but it is sheldom to see sample weight normalize in large scale household surveys. One major downside of normalized weights is the Note that estimation of totals does not make sense with normalized weights.\n\n\n\nfull_sample[\"norm_weight\"] = \\\n    SampleWeight().normalize(samp_weight=full_sample[\"nr_weight\"])\nfull_sample[[\"cluster\", \"region\", \"nr_weight\", \"norm_weight\"]].head(25)\n\nprint((full_sample.shape[0], full_sample[\"norm_weight\"].sum()))\n\n(150, np.float64(150.0))\n\n\nWhen normalize() is called with only the parameter sample_weight then the sample weights are normalize to sum to the length of the sample weight vector.\n\nfull_sample[\"norm_weight2\"] = \\\n    SampleWeight().normalize(\n        samp_weight=full_sample[\"nr_weight\"], \n        control=300\n        )\n\nprint(full_sample[\"norm_weight2\"].sum())\n\n300.0\n\n\n\nfull_sample[\"norm_weight3\"] = SampleWeight().normalize(\n    samp_weight=full_sample[\"nr_weight\"], \n    domain=full_sample[\"region\"]\n    )\n\nweight_sum = full_sample.groupby([\"region\"]).sum()\nweight_sum.reset_index(inplace=True)\nweight_sum[[\"region\", \"nr_weight\", \"norm_weight\", \"norm_weight3\"]]\n\n\n\n\n\n\n\n\nregion\nnr_weight\nnorm_weight\nnorm_weight3\n\n\n\n\n0\nEast\n3698.703391\n38.419768\n45.0\n\n\n1\nNorth\n1454.250000\n15.105820\n30.0\n\n\n2\nSouth\n2801.889620\n29.104239\n45.0\n\n\n3\nWest\n6485.783333\n67.370173\n30.0\n\n\n\n\n\n\n\n\nnorm_level = {\"East\": 10, \"North\": 20, \"South\": 30, \"West\": 50}\n\nfull_sample[\"norm_weight4\"] = SampleWeight().normalize(\n    samp_weight=full_sample[\"nr_weight\"], \n    control=norm_level, \n    domain=full_sample[\"region\"]\n    )\n\nweight_sum = full_sample.groupby([\"region\"]).sum()\nweight_sum.reset_index(inplace=True)\nweight_sum[[\"region\", \"nr_weight\", \"norm_weight\", \"norm_weight3\", \"norm_weight4\",]]\n\n\n\n\n\n\n\n\nregion\nnr_weight\nnorm_weight\nnorm_weight3\nnorm_weight4\n\n\n\n\n0\nEast\n3698.703391\n38.419768\n45.0\n10.0\n\n\n1\nNorth\n1454.250000\n15.105820\n30.0\n20.0\n\n\n2\nSouth\n2801.889620\n29.104239\n45.0\n30.0\n\n\n3\nWest\n6485.783333\n67.370173\n30.0\n50.0",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Weighting",
      "Sample Weight Adjustments"
    ]
  },
  {
    "objectID": "pages/estimation_taylor.html",
    "href": "pages/estimation_taylor.html",
    "title": "Taylor-based Estimation",
    "section": "",
    "text": "Samplics’s class TaylorEstimator uses linearization methods to estimate the variance of population parameters.\n\nfrom samplics.datasets import load_nhanes2\nfrom samplics.estimation import TaylorEstimator\n\nfrom samplics.utils.types import PopParam\n\n\n# Load Nhanes sample data\nnhanes2_dict = load_nhanes2()\nnhanes2 = nhanes2_dict[\"data\"]\n\nnhanes2.head(15)\n\n\n\n\n\n\n\n\nstratid\npsuid\nrace\nhighbp\nhighlead\nzinc\ndiabetes\nfinalwgt\n\n\n\n\n0\n1\n1\n1\n0\nNaN\n104.0\n0.0\n8995\n\n\n1\n1\n1\n1\n0\n0.0\n111.0\n0.0\n25964\n\n\n2\n1\n1\n3\n0\nNaN\n102.0\n0.0\n8752\n\n\n3\n1\n1\n1\n1\nNaN\n109.0\n1.0\n4310\n\n\n4\n1\n1\n1\n0\n0.0\n99.0\n0.0\n9011\n\n\n5\n1\n1\n1\n1\nNaN\n101.0\n0.0\n4310\n\n\n6\n1\n1\n1\n0\n0.0\n93.0\n0.0\n3201\n\n\n7\n1\n1\n1\n1\nNaN\n83.0\n0.0\n25386\n\n\n8\n1\n1\n1\n0\nNaN\n98.0\n0.0\n12102\n\n\n9\n1\n1\n2\n0\n0.0\n98.0\n0.0\n4312\n\n\n10\n1\n1\n1\n1\nNaN\n92.0\n0.0\n4031\n\n\n11\n1\n1\n2\n0\n0.0\n90.0\n0.0\n3628\n\n\n12\n1\n1\n1\n0\nNaN\n101.0\n0.0\n28590\n\n\n13\n1\n1\n1\n0\n0.0\nNaN\n0.0\n22754\n\n\n14\n1\n1\n2\n0\n1.0\n123.0\n0.0\n7119\n\n\n\n\n\n\n\nUsing samplics, we can estimate the average level of zinc in the blood using the following\n\nzinc_mean_str = TaylorEstimator(PopParam.mean)\nzinc_mean_str.estimate(\n    y=nhanes2[\"zinc\"],\n    samp_weight=nhanes2[\"finalwgt\"],\n    stratum=nhanes2[\"stratid\"],\n    psu=nhanes2[\"psuid\"],\n    remove_nan=True,\n)\n\nprint(zinc_mean_str)\n\nSAMPLICS - Estimation of Mean\n\nNumber of strata: 31\nNumber of psus: 62\nDegree of freedom: 31\n\n     MEAN       SE       LCI       UCI       CV\n87.182067 0.494483 86.173563 88.190571 0.005672\n\n\nThe results of the estimation are stored in the dictionary zinc_mean_str. The users can covert the main estimation information into a pd.DataFrame by using the method to_dataframe().\n\nzinc_mean_str.to_dataframe()\n\n\n\n\n\n\n\n\n_param\n_estimate\n_stderror\n_lci\n_uci\n_cv\n\n\n\n\n0\nPopParam.mean\n87.182067\n0.494483\n86.173563\n88.190571\n0.005672\n\n\n\n\n\n\n\nThe method to_dataframe() is more useful for domain estimation by producing a table where which row is a level of the domain of interest, as shown below.\n\nzinc_mean_by_race = TaylorEstimator(PopParam.mean)\nzinc_mean_by_race.estimate(\n    y=nhanes2[\"zinc\"],\n    samp_weight=nhanes2[\"finalwgt\"],\n    stratum=nhanes2[\"stratid\"],\n    domain=nhanes2[\"race\"],\n    psu=nhanes2[\"psuid\"],\n    remove_nan=True,\n)\n\nzinc_mean_by_race.to_dataframe()\n\n\n\n\n\n\n\n\n_param\n_domain\n_estimate\n_stderror\n_lci\n_uci\n_cv\n\n\n\n\n0\nPopParam.mean\n1\n87.495389\n0.479196\n86.518062\n88.472716\n0.005477\n\n\n1\nPopParam.mean\n2\n85.085744\n1.165209\n82.709286\n87.462203\n0.013695\n\n\n2\nPopParam.mean\n3\n83.570910\n1.585463\n80.337338\n86.804483\n0.018971\n\n\n\n\n\n\n\nLet’s remove the stratum parameter then we get\n\nzinc_mean_nostr = TaylorEstimator(PopParam.mean)\nzinc_mean_nostr.estimate(\n    y=nhanes2[\"zinc\"], \n    samp_weight=nhanes2[\"finalwgt\"], \n    psu=nhanes2[\"psuid\"], \n    remove_nan=True\n)\n\nprint(zinc_mean_nostr)\n\nSAMPLICS - Estimation of Mean\n\nNumber of strata: 1\nNumber of psus: 2\nDegree of freedom: 1\n\n     MEAN       SE       LCI       UCI       CV\n87.182067 0.742622 77.746158 96.617976 0.008518",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Population Parameters Estimation",
      "Taylor-based Estimation"
    ]
  },
  {
    "objectID": "pages/size.html",
    "href": "pages/size.html",
    "title": "Overview",
    "section": "",
    "text": "Sample size calculation and allocation is usually one of the first steps in conducting a survey. Sample size calculations allow the survey implementers to collect enough data o achieve targeted precision levels. To do so, the sample size calculation should follow as close as possible the actual sampling design and take into account imperfections of survey implementations such as non response. Since sample sizes are calculated prior to implementing the survey, many of the input data needed to calculate sample sizes are obtained from previous experience and from reasonable assumptions.\nIn this tutorial, we illustrate the calculation and allocation of some common sample size methods. Users can found additional details from Chow et al. (2018) and Ryan (2013).\nSection 1: Sample size calculation for stage sampling \n\n\n\n\nReferences\n\nChow, S., Shao J., Wang H., and Y. Lokhnygina. 2018. Sample Size Calculations in Clinical Research, Third Edition. Chapman; Hall/CRC. https://doi.org/10.1201/9781315183084.\n\n\nRyan, T P. 2013. Sample Size Determination and Power. John Wiley & Sons, Inc. https://doi.org/10.1002/9781118439241.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Size Calculation",
      "Overview"
    ]
  },
  {
    "objectID": "pages/getting_started.html",
    "href": "pages/getting_started.html",
    "title": "Getting Started",
    "section": "",
    "text": "Samplics is a python package for selecting, weighting and analyzing sample obtained from complex sampling design.\n\nInstallation\n\npip install samplics\n\nif both Python 2.x and python 3.x are installed on your computer, you may have to use:\n\npip3 install samplics\n\n\n\nDependencies\nPython versions 3.10.x or newer and the following packages:\n\nnumpy\npandas\npolars\nmatplotlib\nstatsmodels\n\n\n\nUsage\nTo select a sample of primary sampling units using PPS method, we can use a code similar to:\n\nimport samplics\nfrom samplics.sampling import SampleSelection\n\npsu_frame = pd.read_csv(\"psu_frame.csv\")\npsu_sample_size = {\"East\":3, \"West\": 2, \"North\": 2, \"South\": 3}\n\npps_design = SampleSelection(\n    method=\"pps-sys\", stratification=True, with_replacement=False\n    )\n\nframe[\"psu_prob\"] = pps_design.inclusion_probs(\n    psu_frame[\"cluster\"],\n    psu_sample_size,\n    psu_frame[\"region\"],\n    psu_frame[\"number_households_census\"]\n    )\n\nTo adjust the design sample weight for nonresponse, we can use a code similar to:\n\nimport samplics\nfrom samplics.weighting import SampleWeight\n\nstatus_mapping = {\n    \"in\": \"ineligible\", \n    \"rr\": \"respondent\", \n    \"nr\": \"non-respondent\", \n    \"uk\":\"unknown\"\n    }\n\nfull_sample[\"nr_weight\"] = SampleWeight().adjust(\n    samp_weight=full_sample[\"design_weight\"],\n    adjust_class=full_sample[\"region\"],\n    resp_status=full_sample[\"response_status\"],\n    resp_dict=status_mapping\n    )\n\n\nimport samplics\nfrom samplics.estimation import TaylorEstimator, ReplicateEstimator\n\nzinc_mean_str = TaylorEstimator(\"mean\").estimate(\n    y=nhanes2f[\"zinc\"],\n    samp_weight=nhanes2f[\"finalwgt\"],\n    stratum=nhanes2f[\"stratid\"],\n    psu=nhanes2f[\"psuid\"],\n    remove_nan=True,\n)\n\nratio_wgt_hgt = ReplicateEstimator(\"brr\", \"ratio\").estimate(\n    y=nhanes2brr[\"weight\"],\n    samp_weight=nhanes2brr[\"finalwgt\"],\n    x=nhanes2brr[\"height\"],\n    rep_weights=nhanes2brr.loc[:, \"brr_1\":\"brr_32\"],\n    remove_nan=True,\n)\n\n\n\nContributing\n\n\nSupport the project\n\n\nLicense\nOpen source MIT",
    "crumbs": [
      "Overview",
      "Getting Started"
    ]
  },
  {
    "objectID": "pages/categorical_ttest.html",
    "href": "pages/categorical_ttest.html",
    "title": "T-test",
    "section": "",
    "text": "The t-test module allows comparing means of continuous variables of interest to known means or across two groups. There are four main types of comparisons.\n\nComparison of one-sample mean to a known mean\nComparison of two groups from the same sample\nComparison of two means from two different samples\nComparison of two paired means\n\nTtest() is the class that implements all four type of comparisons. To run a comparison, the user call the method compare() with the appropriate parameters.\n\nfrom pprint import pprint\n\nfrom samplics.datasets import load_auto\nfrom samplics.categorical.comparison import Ttest\n\n\nComparison of one-sample mean to a knowm mean\nFor this comparison, the mean of a continuous variable, i.e. mpg, is compared to a know mean. In the example below, the user is testing whether the average mpg is equal to 20. Hence, the null hypothesis is H0: mean(mpg) = 20. There are three possible alternatives for this null hypotheses:\n\nHa: mean(mpg) &lt; 20 (less_than alternative)\nHa: mean(mpg) &gt; 20 (greater_than alternative)\nHa: mean(mpg) != 20 (not_equal alternative)\n\nAll three alternatives are automatically computed by the method compare(). This behavior is similar across the four type of comparisons.\n\n# Load Auto sample data\nauto_dict = load_auto()\nauto = auto_dict[\"data\"]\nmpg = auto[\"mpg\"]\n\none_sample_known_mean = Ttest(samp_type=\"one-sample\")\none_sample_known_mean.compare(y=mpg, known_mean=20)\n\nprint(one_sample_known_mean)\n\n\nDesign-based One-Sample T-test\n Null hypothesis (Ho): mean = 20\n t statistics: 1.9289\n Degrees of freedom: 73.00\n Alternative hypothesis (Ha):\n  Prob(T &lt; t) = 0.9712\n  Prob(|T| &gt; |t|) = 0.0576\n  Prob(T &gt; t) = 0.0288 \n\n Nb. Obs  PopParam.mean  Std. Error  Std. Dev.  Lower CI  Upper CI\n      74      21.297297    0.672551   5.785503 19.956905  22.63769\n\n\n\nThe print below shows the information encapsulated in the object. point_est provides the sample mean. Similarly, stderror, stddev, lower_ci, and upper_ci provide the standard error, standard deviation, lower bound confidence interval (CI), and upper bound CI, respectively. The class member stats provides the statistics related to the three t-tests (for the three alternative hypothesis). There is additional information encapsulated in the object as shown below.\n\npprint(one_sample_known_mean.__dict__)\n\n{'alpha': 0.05,\n 'deff': {},\n 'group_levels': {},\n 'group_names': [],\n 'lower_ci': np.float64(19.95690491373974),\n 'paired': False,\n 'point_est': 21.2972972972973,\n 'samp_type': 'one-sample',\n 'stats': {'df': 73,\n           'known_mean': 20,\n           'number_obs': 74,\n           'p_value': {'greater_than': np.float64(0.02881433507499831),\n                       'less_than': np.float64(0.9711856649250017),\n                       'not_equal': np.float64(0.05762867014999661)},\n           't': 1.9289200809064198},\n 'stddev': 5.785503209735141,\n 'stderror': 0.6725510870764976,\n 'upper_ci': np.float64(22.637689680854855),\n 'vars_names': ['mpg']}\n\n\n\n\nComparison of two groups from the same sample\nThis type of comparison is used when the two groups are from the sample. For example, after running a survey, the user want to know if the domestic cars have the same mpg on average compare to the foreign cars. The parameter group indicates the categorical variable. NB: note that, at this point, Ttest() does not take into account potential dependencies between groups.\n\nforeign = auto[\"foreign\"]\n\none_sample_two_groups = Ttest(samp_type=\"one-sample\")\none_sample_two_groups.compare(y=mpg, group=foreign)\n\nprint(one_sample_two_groups)\n\n\nDesign-based One-Sample T-test\n Null hypothesis (Ho): mean(Domestic) = mean(Foreign) \n Equal variance assumption:\n  t statistics: -3.6632\n  Degrees of freedom: 72.00\n  Alternative hypothesis (Ha):\n   Prob(T &lt; t) = 0.0002\n   Prob(|T| &gt; |t|) = 0.0005\n   Prob(T &gt; t) = 0.9998\n Unequal variance assumption:\n  t statistics: -3.2245\n  Degrees of freedom: 30.81\n  Alternative hypothesis (Ha):\n   Prob(T &lt; t) = 0.0015\n   Prob(|T| &gt; |t|) = 0.0030\n   Prob(T &gt; t) = 0.9985 \n\n   Group  Nb. Obs  PopParam.mean  Std. Error  Std. Dev.  Lower CI  Upper CI\nDomestic       52      19.826923    0.655868   4.729532 18.519780 21.134066\n Foreign       22      24.772727    1.386503   6.503276 22.009431 27.536024\n\n\n\nSince there are two groups for this comparison, the sample mean, standard error, standard deviation, lower bound CI, and upper bound CI are provided by group as Python dictionaries. The class member stats provides statistics for the comparison assuming both equal and unequal variances.\n\nprint(\"These are the group means for mpg:\")\npprint(one_sample_two_groups.point_est)\n\nThese are the group means for mpg:\n{'Domestic': 19.826923076923077, 'Foreign': 24.772727272727273}\n\n\n\nprint(f\"These are the group standard error for mpg:\")\npprint(one_sample_two_groups.stderror)\n\nThese are the group standard error for mpg:\n{'Domestic': 0.6558681110509441, 'Foreign': 1.3865030562044942}\n\n\n\nprint(\"These are the group standard deviation for mpg:\")\npprint(one_sample_two_groups.stddev)\n\nThese are the group standard deviation for mpg:\n{'Domestic': 4.7295322086717775, 'Foreign': 6.50327578586491}\n\n\n\nprint(\"These are the computed statistics:\")\npprint(one_sample_two_groups.stats)\n\nThese are the computed statistics:\n{'df_eq_variance': np.int64(72),\n 'df_uneq_variance': np.float64(30.814287872636015),\n 'number_obs': {'Domestic': np.int64(52), 'Foreign': np.int64(22)},\n 'p_value_eq_variance': {'greater_than': np.float64(0.9997637712766184),\n                         'less_than': np.float64(0.00023622872338158258),\n                         'not_equal': np.float64(0.00047245744676316517)},\n 'p_value_uneq_variance': {'greater_than': np.float64(0.9985090924569335),\n                           'less_than': np.float64(0.00149090754306649),\n                           'not_equal': np.float64(0.00298181508613298)},\n 't_eq_variance': -3.663245852011623,\n 't_uneq_variance': -3.2245353733260638}\n\n\n\n\nComparison of two means from two different samples\nThis type of comparison should be used when the two groups come from different samples or different strata. The group are assumed independent. Otherwise, the information is similar to the previous test. Note that, when instantiating the class, we used samp_type=\"two-sample\".\n\ntwo_samples_unpaired = Ttest(samp_type=\"two-sample\", paired=False)\ntwo_samples_unpaired.compare(y=mpg, group=foreign)\n\nprint(two_samples_unpaired)\n\n\nDesign-based Two-Sample T-test\n Null hypothesis (Ho): mean(Domestic) = mean(Foreign) \n Equal variance assumption:\n  t statistics: -3.6308\n  Degrees of freedom: 72.00\n  Alternative hypothesis (Ha):\n   Prob(T &lt; t) = 0.0003\n   Prob(|T| &gt; |t|) = 0.0005\n   Prob(T &gt; t) = 0.9997\n Unequal variance assumption:\n  t statistics: -3.1797\n  Degrees of freedom: 30.55\n  Alternative hypothesis (Ha):\n   Prob(T &lt; t) = 0.0017\n   Prob(|T| &gt; |t|) = 0.0034\n   Prob(T &gt; t) = 0.9983 \n\n   Group  Nb. Obs  PopParam.mean  Std. Error  Std. Dev.  Lower CI  Upper CI\nDomestic       52      19.826923    0.657777   4.743297 18.506381 21.147465\n Foreign       22      24.772727    1.409510   6.611187 21.841491 27.703963\n\n\n\n\nprint(\"These are the group means for mpg:\")\npprint(two_samples_unpaired.point_est)\n\nThese are the group means for mpg:\n{'Domestic': 19.826923076923077, 'Foreign': 24.772727272727273}\n\n\n\nprint(\"These are the group standard error for mpg:\") \npprint(two_samples_unpaired.stderror)\n\nThese are the group standard error for mpg:\n{'Domestic': 0.6577769784877485, 'Foreign': 1.409509782735444}\n\n\n\nprint(\"These are the group standard deviation for mpg:\")\npprint(two_samples_unpaired.stddev)\n\nThese are the group standard deviation for mpg:\n{'Domestic': 4.743297247514701, 'Foreign': 6.611186898567625}\n\n\n\nprint(\"These are the computed statistics:\")\npprint(two_samples_unpaired.stats)\n\nThese are the computed statistics:\n{'df_eq_variance': np.int64(72),\n 'df_uneq_variance': np.float64(30.54627772512107),\n 'number_obs': {'Domestic': np.int64(52), 'Foreign': np.int64(22)},\n 'p_value_eq_variance': {'greater_than': np.float64(0.9997372920330829),\n                         'less_than': np.float64(0.00026270796691710003),\n                         'not_equal': np.float64(0.0005254159338342001)},\n 'p_value_uneq_variance': {'greater_than': np.float64(0.9983149592187673),\n                           'less_than': np.float64(0.0016850407812326064),\n                           'not_equal': np.float64(0.003370081562465213)},\n 't_eq_variance': -3.6308484477318372,\n 't_uneq_variance': -3.1796851846684073}\n\n\n\n\nComparison of two paired means\nWhen two measures are taken from the same observations, the paired t-test is appropriate for comparing the means.\n\ntwo_samples_paired = Ttest(samp_type=\"two-sample\", paired=True)\ntwo_samples_paired.compare(y=auto[[\"y1\", \"y2\"]], group=foreign)\n\nprint(two_samples_paired)\n\n\nDesign-based Two-Sample T-test\n Null hypothesis (Ho): mean(Diff = y1 - y2) = 0\n t statistics: 0.8733\n Degrees of freedom: 73.00\n Alternative hypothesis (Ha):\n  Prob(T &lt; t) = 0.8073\n  Prob(|T| &gt; |t|) = 0.3853\n  Prob(T &gt; t) = 0.1927 \n\n Nb. Obs  PopParam.mean   Std. Error  Std. Dev.      Lower CI  Upper CI\n      74   4.054054e-07 4.641962e-07   0.000004 -5.197363e-07  0.000001\n\n\n\nvarnames can be used rename the variables\n\ny1 = auto[\"y1\"]\ny2 = auto[\"y2\"]\n\ntwo_samples_paired = Ttest(samp_type=\"two-sample\", paired=True)\ntwo_samples_paired.compare(\n    y=[y1, y2], \n    varnames= [\"group_1\", \"gourp_2\"], \n    group=foreign\n    )\n\nprint(two_samples_paired)\n\n\nDesign-based Two-Sample T-test\n Null hypothesis (Ho): mean(Diff = group_1 - gourp_2) = 0\n t statistics: 0.8733\n Degrees of freedom: 73.00\n Alternative hypothesis (Ha):\n  Prob(T &lt; t) = 0.8073\n  Prob(|T| &gt; |t|) = 0.3853\n  Prob(T &gt; t) = 0.1927 \n\n Nb. Obs  PopParam.mean   Std. Error  Std. Dev.      Lower CI  Upper CI\n      74   4.054054e-07 4.641962e-07   0.000004 -5.197363e-07  0.000001",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Categorical Data Analysis",
      "T-test"
    ]
  },
  {
    "objectID": "pages/sae_area.html",
    "href": "pages/sae_area.html",
    "title": "Area Level Modeling",
    "section": "",
    "text": "Small area estimation (SAE) are useful techniques when the sample sizes are not sufficient to provide reliable direct domain estimates given the sampling design. In this tutorial, the direct estimates refer to estimates obtained from the design-based approach. It usually consists of applying adjusted design weights to the variable of interest to compute sample parameters as estimates of equivalent population parameters. When auxiliary information is available, we can use model assisted survey methods can be used to estimate population parameters.\nIn this tutorial, we will go futher and use modeling techniques to produce domains estimates. For the area level model, the modeling is done at the area level using generalized linear mixed models. The sections below shows how to use the EblupAreaModel class from the samplics package to produce area level estimates.\n\n\nTo illustrate the EblupAreaModel class, we will use the Milk Expenditure dataset used in Rao and Molina (2015). As mentioned in the book, this dataset was originally used by Arora and Lahiri (1997) and later by You and Chapman (2006). For the R users, this dataset is also used by the R package sae (https://cran.r-project.org/web/packages/sae/index.html).\nThe Milk Expenditure data contains 43 observations on the average expenditure on fresh milk for the year 1989. The datasets has the following values: major area representing (major_area), small area (small_area), sample size (samp_size), direct survey estimates of average expenditure (direct_est), standard error of the direct estimate (std_error), and coefficient of variation of the direct estimates (coef_variance).\n\nfrom samplics.datasets import load_expenditure_milk\nfrom samplics.sae import EblupAreaModel\n\nfrom samplics.utils.types import FitMethod\n\n\n# Load Expenditure on Milk sample data\nmilk_exp_dict = load_expenditure_milk()\nmilk_exp = milk_exp_dict[\"data\"]\n\nnb_obs = 15\nprint(f\"First {nb_obs} observations of the Milk Expendure dataset\\n\")\nmilk_exp.tail(nb_obs)\n\nFirst 15 observations of the Milk Expendure dataset\n\n\n\n\n\n\n\n\n\n\nmajor_area\nsmall_area\nsamp_size\ndirect_est\nstd_error\ncoef_var\n\n\n\n\n28\n4\n29\n238\n0.796\n0.106\n0.133\n\n\n29\n4\n30\n207\n0.565\n0.089\n0.158\n\n\n30\n4\n31\n165\n0.886\n0.225\n0.254\n\n\n31\n4\n32\n153\n0.952\n0.205\n0.215\n\n\n32\n4\n33\n210\n0.807\n0.119\n0.147\n\n\n33\n4\n34\n383\n0.582\n0.067\n0.115\n\n\n34\n4\n35\n255\n0.684\n0.106\n0.155\n\n\n35\n4\n36\n226\n0.787\n0.126\n0.160\n\n\n36\n4\n37\n224\n0.440\n0.092\n0.209\n\n\n37\n4\n38\n212\n0.759\n0.132\n0.174\n\n\n38\n4\n39\n211\n0.770\n0.100\n0.130\n\n\n39\n4\n40\n179\n0.800\n0.113\n0.141\n\n\n40\n4\n41\n312\n0.756\n0.083\n0.110\n\n\n41\n4\n42\n241\n0.865\n0.121\n0.140\n\n\n42\n4\n43\n205\n0.640\n0.129\n0.202\n\n\n\n\n\n\n\n\n\n\nAs shown in the milk expenditure datasets, some of the coefficients of variation are not small which indicates unstability of the direct survey estimates. Hence, we can try to reduce the variability of the estimates by smoothing them through modeling. For illustration purpose, we will model the average expenditure on milk using the major areas as auxiliary variables.\nFirst, we use the method fit() to estimate the model parameters. The pandas’s method get_dummies() create a matrix with dummy values (0 and 1) from the categorical variable major_area.\n\narea = milk_exp[\"small_area\"]\nyhat = milk_exp[\"direct_est\"]\n\nimport pandas as pd\nX = pd.get_dummies(milk_exp[\"major_area\"],drop_first=True)\nsigma_e = milk_exp[\"std_error\"]\n\n## REML method\nfh_model_reml = EblupAreaModel(method=FitMethod.reml)\nfh_model_reml.fit(\n    yhat=yhat, X=X, area=area, error_std=sigma_e, intercept=True, tol=1e-8,\n)\n\n\nfrom pprint import pprint\n\nprint(f\"The estimated fixed effects are:\") \npprint(fh_model_reml.fixed_effects)\n\nThe estimated fixed effects are:\narray([ 0.96818899,  0.13278031,  0.22694622, -0.24130104])\n\n\n\nprint(\"The estimated standard error of the area random effects is:\")\npprint(fh_model_reml.re_std)\n\nThe estimated standard error of the area random effects is:\nnp.float64(0.1361996136767915)\n\n\n\nprint(\"The convergence statistics are:\")\npprint(fh_model_reml.convergence)\n\nThe convergence statistics are:\n{'achieved': np.True_,\n 'iterations': 9,\n 'precision': np.float64(1.979926775564084e-09)}\n\n\n\nprint(\"The goodness of fit statistics are:\") \npprint(fh_model_reml.goodness)\n\nThe goodness of fit statistics are:\n{'AIC': 30.80694881797811,\n 'BIC': 41.37414951213948,\n 'loglike': -9.403474408989055}\n\n\n\nfh_model_reml.predict(\n    X=X, area=area, intercept=True\n)\n\npprint(fh_model_reml.area_est)\n\n{np.int64(1): np.float64(1.0219705441558895),\n np.int64(2): np.float64(1.047601951445677),\n np.int64(3): np.float64(1.0679514263086396),\n np.int64(4): np.float64(0.7608165650758552),\n np.int64(5): np.float64(0.8461570437704427),\n np.int64(6): np.float64(0.9743727061135175),\n np.int64(7): np.float64(1.0584526719577583),\n np.int64(8): np.float64(1.0977762561870668),\n np.int64(9): np.float64(1.2215454894600388),\n np.int64(10): np.float64(1.1951460148495627),\n np.int64(11): np.float64(0.7852149191679922),\n np.int64(12): np.float64(1.2139462053925374),\n np.int64(13): np.float64(1.2096597208051414),\n np.int64(14): np.float64(0.9834964411924453),\n np.int64(15): np.float64(1.1864247095921692),\n np.int64(16): np.float64(1.1556981139181453),\n np.int64(17): np.float64(1.2263412506598241),\n np.int64(18): np.float64(1.2856489886705895),\n np.int64(19): np.float64(1.2363248408581458),\n np.int64(20): np.float64(1.2349601393920562),\n np.int64(21): np.float64(1.090301627484137),\n np.int64(22): np.float64(1.1923057228337588),\n np.int64(23): np.float64(1.1216467667588341),\n np.int64(24): np.float64(1.2230297218953323),\n np.int64(25): np.float64(1.1938054443945294),\n np.int64(26): np.float64(0.7627195896418987),\n np.int64(27): np.float64(0.7649551532185552),\n np.int64(28): np.float64(0.7338443880846521),\n np.int64(29): np.float64(0.7699295541643933),\n np.int64(30): np.float64(0.6134416233555452),\n np.int64(31): np.float64(0.7695560722792778),\n np.int64(32): np.float64(0.7958253116952814),\n np.int64(33): np.float64(0.7723188477263883),\n np.int64(34): np.float64(0.610230068308824),\n np.int64(35): np.float64(0.7001781896510536),\n np.int64(36): np.float64(0.7592788104101726),\n np.int64(37): np.float64(0.5298863364482942),\n np.int64(38): np.float64(0.743446678039724),\n np.int64(39): np.float64(0.7548996331139428),\n np.int64(40): np.float64(0.7701919657191105),\n np.int64(41): np.float64(0.7481164238473628),\n np.int64(42): np.float64(0.8040775158100446),\n np.int64(43): np.float64(0.6810868850580024)}\n\n\nWe can use the utility method to_dataframe() to output the estimates as a pandas dataframe. The function provides the area, the estimate and its MSE estimates. We can use col_names to customize the name of the columns. For example, using col_names = [\"small_area\", \"eblup_estimate\", \"eblup_mse\"]. Otherwise, if col_names is not provided, “_area”, “_estimates” and “_mse” are used as defaults.\n\nmilk_est_reml = fh_model_reml.to_dataframe(\n    col_names = [\"parameter\", \"small_area\", \"eblup_estimate\", \"eblup_mse\"]\n    )\nprint(\"The dataframe version of the area level estimates:\")\nprint(milk_est_reml)\n\nThe dataframe version of the area level estimates:\n   parameter  small_area  eblup_estimate  eblup_mse\n0       mean           1        1.021971   0.013460\n1       mean           2        1.047602   0.005373\n2       mean           3        1.067951   0.005702\n3       mean           4        0.760817   0.008542\n4       mean           5        0.846157   0.009580\n5       mean           6        0.974373   0.011671\n6       mean           7        1.058453   0.015926\n7       mean           8        1.097776   0.010587\n8       mean           9        1.221545   0.014184\n9       mean          10        1.195146   0.014902\n10      mean          11        0.785215   0.007694\n11      mean          12        1.213946   0.016337\n12      mean          13        1.209660   0.012563\n13      mean          14        0.983496   0.012117\n14      mean          15        1.186425   0.012031\n15      mean          16        1.155698   0.011709\n16      mean          17        1.226341   0.010860\n17      mean          18        1.285649   0.013691\n18      mean          19        1.236325   0.011035\n19      mean          20        1.234960   0.013080\n20      mean          21        1.090302   0.009949\n21      mean          22        1.192306   0.017244\n22      mean          23        1.121647   0.011292\n23      mean          24        1.223030   0.013625\n24      mean          25        1.193805   0.008066\n25      mean          26        0.762720   0.009205\n26      mean          27        0.764955   0.009205\n27      mean          28        0.733844   0.016477\n28      mean          29        0.769930   0.007801\n29      mean          30        0.613442   0.006099\n30      mean          31        0.769556   0.015442\n31      mean          32        0.795825   0.014658\n32      mean          33        0.772319   0.009025\n33      mean          34        0.610230   0.003871\n34      mean          35        0.700178   0.007801\n35      mean          36        0.759279   0.009646\n36      mean          37        0.529886   0.006404\n37      mean          38        0.743447   0.010156\n38      mean          39        0.754900   0.007210\n39      mean          40        0.770192   0.008470\n40      mean          41        0.748116   0.005485\n41      mean          42        0.804078   0.009205\n42      mean          43        0.681087   0.009904\n\n\nWe could also fit the model parameters using the maximum likelihood (ML) method which will impact the MSE estimation as well. To estimate the area means using the ML methdo, we only need to set method=“ML” then run the prediction as follows.\n\n## ML method\nfh_model_ml = EblupAreaModel(method=FitMethod.ml)\nfh_model_ml.fit(\n    yhat=yhat, X=X, area=area, error_std=sigma_e, intercept=True, tol=1e-8,\n)\n\nmilk_est_ml = fh_model_ml.predict(\n    X=X, area=area, intercept=True\n)\n\nmilk_est_ml = fh_model_ml.to_dataframe(\n    col_names = [\"parameter\", \"small_area\", \"eblup_estimate\", \"eblup_mse\"]\n    )\n\n\nprint(\"The dataframe version of the ML area level estimates:\")\nprint(milk_est_ml)\n\nThe dataframe version of the ML area level estimates:\n   parameter  small_area  eblup_estimate  eblup_mse\n0       mean           1        1.016228   0.013559\n1       mean           2        1.043736   0.005507\n2       mean           3        1.062868   0.005844\n3       mean           4        0.775205   0.008724\n4       mean           5        0.855398   0.009761\n5       mean           6        0.973593   0.011823\n6       mean           7        1.047581   0.015908\n7       mean           8        1.095370   0.010806\n8       mean           9        1.205566   0.014324\n9       mean          10        1.181391   0.015012\n10      mean          11        0.803191   0.007901\n11      mean          12        1.196940   0.016378\n12      mean          13        1.196292   0.012752\n13      mean          14        0.991332   0.012316\n14      mean          15        1.186879   0.012174\n15      mean          16        1.159004   0.011859\n16      mean          17        1.223267   0.011025\n17      mean          18        1.275615   0.013783\n18      mean          19        1.232324   0.011197\n19      mean          20        1.230486   0.013193\n20      mean          21        1.098496   0.010124\n21      mean          22        1.192161   0.017163\n22      mean          23        1.127931   0.011450\n23      mean          24        1.219661   0.013719\n24      mean          25        1.193628   0.008241\n25      mean          26        0.759102   0.009332\n26      mean          27        0.761161   0.009332\n27      mean          28        0.731587   0.016359\n28      mean          29        0.766310   0.007931\n29      mean          30        0.619089   0.006215\n30      mean          31        0.763002   0.015376\n31      mean          32        0.786464   0.014629\n32      mean          33        0.768021   0.009153\n33      mean          34        0.614095   0.003944\n34      mean          35        0.701300   0.007931\n35      mean          36        0.755792   0.009768\n36      mean          37        0.540557   0.006525\n37      mean          38        0.741155   0.010271\n38      mean          39        0.752476   0.007338\n39      mean          40        0.766282   0.008601\n40      mean          41        0.746552   0.005592\n41      mean          42        0.797209   0.009332\n42      mean          43        0.684069   0.010022\n\n\nSimilar, we can use the Fay-Herriot method as follows\n\n## FH method\nfh_model_fh = EblupAreaModel(method=FitMethod.fh)\nfh_model_fh.fit(\n    yhat=yhat, X=X, area=area, error_std=sigma_e, intercept=True, tol=1e-8,\n)\n\nmilk_est_fh = fh_model_fh.predict(\n    X=X, area=area, intercept=True\n)\n\nmilk_est_fh = fh_model_fh.to_dataframe(col_names = [\"parameter\", \"small_area\", \"eblup_estimate\", \"eblup_mse\"])\n\n\nprint(f\"The dataframe version of the ML area level estimates:\")\nprint(milk_est_fh)\n\nThe dataframe version of the ML area level estimates:\n   parameter  small_area  eblup_estimate  eblup_mse\n0       mean           1        1.017976   0.012757\n1       mean           2        1.044964   0.005314\n2       mean           3        1.064481   0.005632\n3       mean           4        0.770692   0.008323\n4       mean           5        0.852512   0.009284\n5       mean           6        0.973826   0.011178\n6       mean           7        1.050857   0.014868\n7       mean           8        1.096165   0.010253\n8       mean           9        1.210505   0.013471\n9       mean          10        1.185640   0.014095\n10      mean          11        0.797569   0.007558\n11      mean          12        1.202150   0.015325\n12      mean          13        1.200459   0.012039\n13      mean          14        0.988971   0.011640\n14      mean          15        1.186745   0.011467\n15      mean          16        1.157992   0.011182\n16      mean          17        1.224223   0.010424\n17      mean          18        1.278680   0.012914\n18      mean          19        1.233566   0.010581\n19      mean          20        1.231860   0.012386\n20      mean          21        1.095955   0.009600\n21      mean          22        1.192213   0.015890\n22      mean          23        1.125997   0.010811\n23      mean          24        1.220695   0.012858\n24      mean          25        1.193687   0.007865\n25      mean          26        0.760244   0.008855\n26      mean          27        0.762358   0.008855\n27      mean          28        0.732288   0.015042\n28      mean          29        0.767459   0.007569\n29      mean          30        0.617310   0.005975\n30      mean          31        0.764997   0.014212\n31      mean          32        0.789315   0.013572\n32      mean          33        0.769376   0.008692\n33      mean          34        0.612861   0.003833\n34      mean          35        0.700962   0.007569\n35      mean          36        0.756891   0.009253\n36      mean          37        0.537193   0.006264\n37      mean          38        0.741882   0.009709\n38      mean          39        0.753251   0.007020\n39      mean          40        0.767519   0.008186\n40      mean          41        0.747059   0.005391\n41      mean          42        0.799363   0.008855\n42      mean          43        0.683161   0.009484",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Small Area Estimation",
      "Area Level Modeling"
    ]
  },
  {
    "objectID": "pages/sae_area.html#milk-expenditure-data",
    "href": "pages/sae_area.html#milk-expenditure-data",
    "title": "Area Level Modeling",
    "section": "",
    "text": "To illustrate the EblupAreaModel class, we will use the Milk Expenditure dataset used in Rao and Molina (2015). As mentioned in the book, this dataset was originally used by Arora and Lahiri (1997) and later by You and Chapman (2006). For the R users, this dataset is also used by the R package sae (https://cran.r-project.org/web/packages/sae/index.html).\nThe Milk Expenditure data contains 43 observations on the average expenditure on fresh milk for the year 1989. The datasets has the following values: major area representing (major_area), small area (small_area), sample size (samp_size), direct survey estimates of average expenditure (direct_est), standard error of the direct estimate (std_error), and coefficient of variation of the direct estimates (coef_variance).\n\nfrom samplics.datasets import load_expenditure_milk\nfrom samplics.sae import EblupAreaModel\n\nfrom samplics.utils.types import FitMethod\n\n\n# Load Expenditure on Milk sample data\nmilk_exp_dict = load_expenditure_milk()\nmilk_exp = milk_exp_dict[\"data\"]\n\nnb_obs = 15\nprint(f\"First {nb_obs} observations of the Milk Expendure dataset\\n\")\nmilk_exp.tail(nb_obs)\n\nFirst 15 observations of the Milk Expendure dataset\n\n\n\n\n\n\n\n\n\n\nmajor_area\nsmall_area\nsamp_size\ndirect_est\nstd_error\ncoef_var\n\n\n\n\n28\n4\n29\n238\n0.796\n0.106\n0.133\n\n\n29\n4\n30\n207\n0.565\n0.089\n0.158\n\n\n30\n4\n31\n165\n0.886\n0.225\n0.254\n\n\n31\n4\n32\n153\n0.952\n0.205\n0.215\n\n\n32\n4\n33\n210\n0.807\n0.119\n0.147\n\n\n33\n4\n34\n383\n0.582\n0.067\n0.115\n\n\n34\n4\n35\n255\n0.684\n0.106\n0.155\n\n\n35\n4\n36\n226\n0.787\n0.126\n0.160\n\n\n36\n4\n37\n224\n0.440\n0.092\n0.209\n\n\n37\n4\n38\n212\n0.759\n0.132\n0.174\n\n\n38\n4\n39\n211\n0.770\n0.100\n0.130\n\n\n39\n4\n40\n179\n0.800\n0.113\n0.141\n\n\n40\n4\n41\n312\n0.756\n0.083\n0.110\n\n\n41\n4\n42\n241\n0.865\n0.121\n0.140\n\n\n42\n4\n43\n205\n0.640\n0.129\n0.202",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Small Area Estimation",
      "Area Level Modeling"
    ]
  },
  {
    "objectID": "pages/sae_area.html#eblup-predictor",
    "href": "pages/sae_area.html#eblup-predictor",
    "title": "Area Level Modeling",
    "section": "",
    "text": "As shown in the milk expenditure datasets, some of the coefficients of variation are not small which indicates unstability of the direct survey estimates. Hence, we can try to reduce the variability of the estimates by smoothing them through modeling. For illustration purpose, we will model the average expenditure on milk using the major areas as auxiliary variables.\nFirst, we use the method fit() to estimate the model parameters. The pandas’s method get_dummies() create a matrix with dummy values (0 and 1) from the categorical variable major_area.\n\narea = milk_exp[\"small_area\"]\nyhat = milk_exp[\"direct_est\"]\n\nimport pandas as pd\nX = pd.get_dummies(milk_exp[\"major_area\"],drop_first=True)\nsigma_e = milk_exp[\"std_error\"]\n\n## REML method\nfh_model_reml = EblupAreaModel(method=FitMethod.reml)\nfh_model_reml.fit(\n    yhat=yhat, X=X, area=area, error_std=sigma_e, intercept=True, tol=1e-8,\n)\n\n\nfrom pprint import pprint\n\nprint(f\"The estimated fixed effects are:\") \npprint(fh_model_reml.fixed_effects)\n\nThe estimated fixed effects are:\narray([ 0.96818899,  0.13278031,  0.22694622, -0.24130104])\n\n\n\nprint(\"The estimated standard error of the area random effects is:\")\npprint(fh_model_reml.re_std)\n\nThe estimated standard error of the area random effects is:\nnp.float64(0.1361996136767915)\n\n\n\nprint(\"The convergence statistics are:\")\npprint(fh_model_reml.convergence)\n\nThe convergence statistics are:\n{'achieved': np.True_,\n 'iterations': 9,\n 'precision': np.float64(1.979926775564084e-09)}\n\n\n\nprint(\"The goodness of fit statistics are:\") \npprint(fh_model_reml.goodness)\n\nThe goodness of fit statistics are:\n{'AIC': 30.80694881797811,\n 'BIC': 41.37414951213948,\n 'loglike': -9.403474408989055}\n\n\n\nfh_model_reml.predict(\n    X=X, area=area, intercept=True\n)\n\npprint(fh_model_reml.area_est)\n\n{np.int64(1): np.float64(1.0219705441558895),\n np.int64(2): np.float64(1.047601951445677),\n np.int64(3): np.float64(1.0679514263086396),\n np.int64(4): np.float64(0.7608165650758552),\n np.int64(5): np.float64(0.8461570437704427),\n np.int64(6): np.float64(0.9743727061135175),\n np.int64(7): np.float64(1.0584526719577583),\n np.int64(8): np.float64(1.0977762561870668),\n np.int64(9): np.float64(1.2215454894600388),\n np.int64(10): np.float64(1.1951460148495627),\n np.int64(11): np.float64(0.7852149191679922),\n np.int64(12): np.float64(1.2139462053925374),\n np.int64(13): np.float64(1.2096597208051414),\n np.int64(14): np.float64(0.9834964411924453),\n np.int64(15): np.float64(1.1864247095921692),\n np.int64(16): np.float64(1.1556981139181453),\n np.int64(17): np.float64(1.2263412506598241),\n np.int64(18): np.float64(1.2856489886705895),\n np.int64(19): np.float64(1.2363248408581458),\n np.int64(20): np.float64(1.2349601393920562),\n np.int64(21): np.float64(1.090301627484137),\n np.int64(22): np.float64(1.1923057228337588),\n np.int64(23): np.float64(1.1216467667588341),\n np.int64(24): np.float64(1.2230297218953323),\n np.int64(25): np.float64(1.1938054443945294),\n np.int64(26): np.float64(0.7627195896418987),\n np.int64(27): np.float64(0.7649551532185552),\n np.int64(28): np.float64(0.7338443880846521),\n np.int64(29): np.float64(0.7699295541643933),\n np.int64(30): np.float64(0.6134416233555452),\n np.int64(31): np.float64(0.7695560722792778),\n np.int64(32): np.float64(0.7958253116952814),\n np.int64(33): np.float64(0.7723188477263883),\n np.int64(34): np.float64(0.610230068308824),\n np.int64(35): np.float64(0.7001781896510536),\n np.int64(36): np.float64(0.7592788104101726),\n np.int64(37): np.float64(0.5298863364482942),\n np.int64(38): np.float64(0.743446678039724),\n np.int64(39): np.float64(0.7548996331139428),\n np.int64(40): np.float64(0.7701919657191105),\n np.int64(41): np.float64(0.7481164238473628),\n np.int64(42): np.float64(0.8040775158100446),\n np.int64(43): np.float64(0.6810868850580024)}\n\n\nWe can use the utility method to_dataframe() to output the estimates as a pandas dataframe. The function provides the area, the estimate and its MSE estimates. We can use col_names to customize the name of the columns. For example, using col_names = [\"small_area\", \"eblup_estimate\", \"eblup_mse\"]. Otherwise, if col_names is not provided, “_area”, “_estimates” and “_mse” are used as defaults.\n\nmilk_est_reml = fh_model_reml.to_dataframe(\n    col_names = [\"parameter\", \"small_area\", \"eblup_estimate\", \"eblup_mse\"]\n    )\nprint(\"The dataframe version of the area level estimates:\")\nprint(milk_est_reml)\n\nThe dataframe version of the area level estimates:\n   parameter  small_area  eblup_estimate  eblup_mse\n0       mean           1        1.021971   0.013460\n1       mean           2        1.047602   0.005373\n2       mean           3        1.067951   0.005702\n3       mean           4        0.760817   0.008542\n4       mean           5        0.846157   0.009580\n5       mean           6        0.974373   0.011671\n6       mean           7        1.058453   0.015926\n7       mean           8        1.097776   0.010587\n8       mean           9        1.221545   0.014184\n9       mean          10        1.195146   0.014902\n10      mean          11        0.785215   0.007694\n11      mean          12        1.213946   0.016337\n12      mean          13        1.209660   0.012563\n13      mean          14        0.983496   0.012117\n14      mean          15        1.186425   0.012031\n15      mean          16        1.155698   0.011709\n16      mean          17        1.226341   0.010860\n17      mean          18        1.285649   0.013691\n18      mean          19        1.236325   0.011035\n19      mean          20        1.234960   0.013080\n20      mean          21        1.090302   0.009949\n21      mean          22        1.192306   0.017244\n22      mean          23        1.121647   0.011292\n23      mean          24        1.223030   0.013625\n24      mean          25        1.193805   0.008066\n25      mean          26        0.762720   0.009205\n26      mean          27        0.764955   0.009205\n27      mean          28        0.733844   0.016477\n28      mean          29        0.769930   0.007801\n29      mean          30        0.613442   0.006099\n30      mean          31        0.769556   0.015442\n31      mean          32        0.795825   0.014658\n32      mean          33        0.772319   0.009025\n33      mean          34        0.610230   0.003871\n34      mean          35        0.700178   0.007801\n35      mean          36        0.759279   0.009646\n36      mean          37        0.529886   0.006404\n37      mean          38        0.743447   0.010156\n38      mean          39        0.754900   0.007210\n39      mean          40        0.770192   0.008470\n40      mean          41        0.748116   0.005485\n41      mean          42        0.804078   0.009205\n42      mean          43        0.681087   0.009904\n\n\nWe could also fit the model parameters using the maximum likelihood (ML) method which will impact the MSE estimation as well. To estimate the area means using the ML methdo, we only need to set method=“ML” then run the prediction as follows.\n\n## ML method\nfh_model_ml = EblupAreaModel(method=FitMethod.ml)\nfh_model_ml.fit(\n    yhat=yhat, X=X, area=area, error_std=sigma_e, intercept=True, tol=1e-8,\n)\n\nmilk_est_ml = fh_model_ml.predict(\n    X=X, area=area, intercept=True\n)\n\nmilk_est_ml = fh_model_ml.to_dataframe(\n    col_names = [\"parameter\", \"small_area\", \"eblup_estimate\", \"eblup_mse\"]\n    )\n\n\nprint(\"The dataframe version of the ML area level estimates:\")\nprint(milk_est_ml)\n\nThe dataframe version of the ML area level estimates:\n   parameter  small_area  eblup_estimate  eblup_mse\n0       mean           1        1.016228   0.013559\n1       mean           2        1.043736   0.005507\n2       mean           3        1.062868   0.005844\n3       mean           4        0.775205   0.008724\n4       mean           5        0.855398   0.009761\n5       mean           6        0.973593   0.011823\n6       mean           7        1.047581   0.015908\n7       mean           8        1.095370   0.010806\n8       mean           9        1.205566   0.014324\n9       mean          10        1.181391   0.015012\n10      mean          11        0.803191   0.007901\n11      mean          12        1.196940   0.016378\n12      mean          13        1.196292   0.012752\n13      mean          14        0.991332   0.012316\n14      mean          15        1.186879   0.012174\n15      mean          16        1.159004   0.011859\n16      mean          17        1.223267   0.011025\n17      mean          18        1.275615   0.013783\n18      mean          19        1.232324   0.011197\n19      mean          20        1.230486   0.013193\n20      mean          21        1.098496   0.010124\n21      mean          22        1.192161   0.017163\n22      mean          23        1.127931   0.011450\n23      mean          24        1.219661   0.013719\n24      mean          25        1.193628   0.008241\n25      mean          26        0.759102   0.009332\n26      mean          27        0.761161   0.009332\n27      mean          28        0.731587   0.016359\n28      mean          29        0.766310   0.007931\n29      mean          30        0.619089   0.006215\n30      mean          31        0.763002   0.015376\n31      mean          32        0.786464   0.014629\n32      mean          33        0.768021   0.009153\n33      mean          34        0.614095   0.003944\n34      mean          35        0.701300   0.007931\n35      mean          36        0.755792   0.009768\n36      mean          37        0.540557   0.006525\n37      mean          38        0.741155   0.010271\n38      mean          39        0.752476   0.007338\n39      mean          40        0.766282   0.008601\n40      mean          41        0.746552   0.005592\n41      mean          42        0.797209   0.009332\n42      mean          43        0.684069   0.010022\n\n\nSimilar, we can use the Fay-Herriot method as follows\n\n## FH method\nfh_model_fh = EblupAreaModel(method=FitMethod.fh)\nfh_model_fh.fit(\n    yhat=yhat, X=X, area=area, error_std=sigma_e, intercept=True, tol=1e-8,\n)\n\nmilk_est_fh = fh_model_fh.predict(\n    X=X, area=area, intercept=True\n)\n\nmilk_est_fh = fh_model_fh.to_dataframe(col_names = [\"parameter\", \"small_area\", \"eblup_estimate\", \"eblup_mse\"])\n\n\nprint(f\"The dataframe version of the ML area level estimates:\")\nprint(milk_est_fh)\n\nThe dataframe version of the ML area level estimates:\n   parameter  small_area  eblup_estimate  eblup_mse\n0       mean           1        1.017976   0.012757\n1       mean           2        1.044964   0.005314\n2       mean           3        1.064481   0.005632\n3       mean           4        0.770692   0.008323\n4       mean           5        0.852512   0.009284\n5       mean           6        0.973826   0.011178\n6       mean           7        1.050857   0.014868\n7       mean           8        1.096165   0.010253\n8       mean           9        1.210505   0.013471\n9       mean          10        1.185640   0.014095\n10      mean          11        0.797569   0.007558\n11      mean          12        1.202150   0.015325\n12      mean          13        1.200459   0.012039\n13      mean          14        0.988971   0.011640\n14      mean          15        1.186745   0.011467\n15      mean          16        1.157992   0.011182\n16      mean          17        1.224223   0.010424\n17      mean          18        1.278680   0.012914\n18      mean          19        1.233566   0.010581\n19      mean          20        1.231860   0.012386\n20      mean          21        1.095955   0.009600\n21      mean          22        1.192213   0.015890\n22      mean          23        1.125997   0.010811\n23      mean          24        1.220695   0.012858\n24      mean          25        1.193687   0.007865\n25      mean          26        0.760244   0.008855\n26      mean          27        0.762358   0.008855\n27      mean          28        0.732288   0.015042\n28      mean          29        0.767459   0.007569\n29      mean          30        0.617310   0.005975\n30      mean          31        0.764997   0.014212\n31      mean          32        0.789315   0.013572\n32      mean          33        0.769376   0.008692\n33      mean          34        0.612861   0.003833\n34      mean          35        0.700962   0.007569\n35      mean          36        0.756891   0.009253\n36      mean          37        0.537193   0.006264\n37      mean          38        0.741882   0.009709\n38      mean          39        0.753251   0.007020\n39      mean          40        0.767519   0.008186\n40      mean          41        0.747059   0.005391\n41      mean          42        0.799363   0.008855\n42      mean          43        0.683161   0.009484",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Small Area Estimation",
      "Area Level Modeling"
    ]
  },
  {
    "objectID": "pages/estimation.html",
    "href": "pages/estimation.html",
    "title": "Overview",
    "section": "",
    "text": "Often the goal of conducting a survey is to estimate parameters of the target population from the survey sample. To appropriately estimate uncertainties associated with the sample estimates, the complex design must be taken into account.\nIn this tutorial, we demonstrate how to use the Samplics APIs to produce point estimates of mean, total, proportion and ratio parameters as well as their associated Taylor-based and replication-based measures of uncertainty.\nSection 1: Taylor-based Estimation  Section 2: Replicate-based Estimation \nWolter (2007) provides a self-contained description of a number of techniques for variance estimation both Taylor and replication based.\n\n\n\n\nReferences\n\nWolter, Kirk M. 2007. Introduction to Variance Estimation, 2nd edn. Springer-Verlag New York, Inc.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Population Parameters Estimation",
      "Overview"
    ]
  },
  {
    "objectID": "pages/examples.html",
    "href": "pages/examples.html",
    "title": "Objectives",
    "section": "",
    "text": "Show real examples of analysis using Samplics"
  },
  {
    "objectID": "index.html",
    "href": "index.html",
    "title": "Introduction",
    "section": "",
    "text": "In large scale surveys, often complex random mechanisms are used to select samples. Estimations obtained from such samples must reflect the random mechanism to ensure accurate calculations. samplics implements a set of sampling techniques for complex survey designs.\nSampling. Since the full population cannot be observed, a sample is selected to estimate population parameters of interest. The assumption is that the sample is representative of the population for the characteristics of interest. The sample size calculation and selection methods in samplics are:\n\nSample size calculation and allocation: Wald and Fleiss methods for proportions.\nEqual probability of selection: simple random sampling (SRS) and systematic selection (SYS)\nProbability proportional to size (PPS): Systematic, Brewer’s method, Hanurav-Vijayan method, Murphy’s method, and Rao-Sampford’s method.\n\nSample weighting. Sample weighting is the main mechanism used in surveys to formalize the representivity of the sample. The design/base weights are usually adjusted to compensate for distortions due nonresponse and other shortcomings of the the sampling design implementation.\n\nWeight adjustment due to nonresponse\nWeight poststratification, calibration and normalization\nWeight replication i.e. Bootstrap, BRR, and Jackknife\n\nPopulation parameters estimation. The estimation of the parameters of interest must reflect the sampling mechanism and the weight adjustments.\n\nTaylor-based procedures\nReplication-based estimation i.e. Boostrap, BRR, and Jackknife\nRegression-based e.g. generalized regression (GREG)\n\nCategorical data analysis. The estimation of the parameters of interest must reflect the sampling mechanism and the weight adjustments.\n\nOne-way and two-way tabulation\nT-test including Rao-Scott adjustment\n\nSmall Area Estimation (SAE). When the sample size is not large enough to produce reliable / stable domain level estimates, SAE techniques can be used to model the output variable of interest to produce domain level estimates.\nContact Information  Mamadou S. Diallo  Twiter: @MamadouSDiallo  Email: msdiallo@samplics.org",
    "crumbs": [
      "Overview",
      "Introduction"
    ]
  },
  {
    "objectID": "pages/tutorial.html",
    "href": "pages/tutorial.html",
    "title": "Outline",
    "section": "",
    "text": "Samplics is a Python package designed to be a comprehensive tool for selecting, weighting and analyzing survey sample data obtained from complex designs. The main objective of this tutorial is to take the user through the Samplics’ APIs main features. We hope that after going through the tutorial, the user will have a good understanding of the APIs and be able to analyze complex sample data using samplics. It is assumed that the user has a basic understanding of Python syntax.\n\n\n\n\n\n\nNote\n\n\n\nThis tutorial is not intended to teach survey sampling methods. To learn survey sampling methods, we refer the user to the reference textbook (Lohr 2021), the UNStats Handbook 2005, Designing Household Survey Samples: Practical Guidelines, and the reference material mentioned throughout this tutorial.\n\n\nThe tutorial is organized into several sections. The sections are fairly independent and users can directly consult the section(s) of interest. However, we recommend that all first time users start with the Section on the Datasets before diving into other sections.\nSection 1: Datasets  Section 2: Sample size calculation      Section 2.1: Sample size for stage design  Section 3: Sample selection  Section 4: Sample weigthing      Section 4.1: Sample weight sdjustment      Section 4.2: Replicate weights  Section 5: Population parameters estimation      Section 5.1: Tabulation      Section 5.2: Unit T-test  Section 6: Categorical data analysis      Section 6.1: Tabulation      Section 6.2: Unit T-test  Section 7: Small area estimation (SAE)      Section 7.1: Area level modeling      Section 7.2: Unit level modeling \n\n\n\n\nReferences\n\nLohr, Sharon L. 2021. Sampling: Design and Analysis, Third Edition. Chapman; Hall/CRC. https://doi.org/https://doi.org/10.1201/9780429298899.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Outline"
    ]
  },
  {
    "objectID": "pages/sae_unit.html",
    "href": "pages/sae_unit.html",
    "title": "Unit Level Modeling",
    "section": "",
    "text": "The Unit Level model refers to a class of SAE techniques that fit the linear mixed model at the sampling unit level. As for the Area Level model, generalized linear mixed models are also the modeling framework for Unit Level model. In this case, given that the model is happening at the sub-area level, both the random effects and unit level standard errors can be estimated from the model. In this tutorial, we will predict the area level means which is a linear parameter.\n\n\nFor this example, we use the county crop data used by Battese, Harter, and Fuller (1988). The datasets contains 37 observations on areas under corn and under soybeans for each of the 12 counties in the north-central of Iowa. Each county was divided in segments and using interviews and LANDSAT satellite data, data on area used for corn and soybeans was obtained. In the unit level data, each observation is a segment with the following variables: county id (county_id), area in hectare under corn (corn_area), area in heactare under soybeans (soybeans_area), number of pixel classified as corn (corn_pixel), and number of pixels classified as soybeans (soybeans_pixel).\n\nfrom samplics.datasets import load_county_crop, load_county_crop_means\nfrom samplics.sae import EblupUnitModel\n\nfrom samplics.utils.types import FitMethod\n\n\n# Load County Crop sample data\ncountycrop_dict = load_county_crop()\ncountycrop = countycrop_dict[\"data\"]\n\nprint(\"First observations from the unit (segment) level crop areas data\")\ncountycrop.head(15)\n\nFirst observations from the unit (segment) level crop areas data\n\n\n\n\n\n\n\n\n\ncounty_id\ncorn_area\nsoybeans_area\ncorn_pixel\nsoybeans_pixel\n\n\n\n\n0\n1\n165.76\n8.09\n374\n55\n\n\n1\n2\n96.32\n106.03\n209\n218\n\n\n2\n3\n76.08\n103.60\n253\n250\n\n\n3\n4\n185.35\n6.47\n432\n96\n\n\n4\n4\n116.43\n63.82\n367\n178\n\n\n5\n5\n162.08\n43.50\n361\n137\n\n\n6\n5\n152.04\n71.43\n288\n206\n\n\n7\n5\n161.75\n42.49\n369\n165\n\n\n8\n6\n92.88\n105.26\n206\n218\n\n\n9\n6\n149.94\n76.49\n316\n221\n\n\n10\n6\n64.75\n174.34\n145\n338\n\n\n11\n7\n127.07\n95.67\n355\n128\n\n\n12\n7\n133.55\n76.57\n295\n147\n\n\n13\n7\n77.70\n93.48\n223\n204\n\n\n14\n8\n206.39\n37.84\n459\n77\n\n\n\n\n\n\n\nIn addition to the unit (segment) level data, we have the small area (county) level averages of the number of pixels classified as corn or soybeans.\n\n# Load County Crop Area Means sample data\ncountycropmeans_dict = load_county_crop_means()\ncountycrop_means = countycropmeans_dict[\"data\"]\n\nprint(f\"County level crop areas averages\")\ncountycrop_means.head(15)\n\nCounty level crop areas averages\n\n\n\n\n\n\n\n\n\ncounty_id\nsamp_segments\npop_segments\nave_corn_pixel\nave_soybeans_pixel\n\n\n\n\n0\n1\n1\n545\n295.29\n189.70\n\n\n1\n2\n1\n566\n300.40\n196.65\n\n\n2\n3\n1\n394\n289.60\n205.28\n\n\n3\n4\n2\n424\n290.74\n220.22\n\n\n4\n5\n3\n564\n318.21\n188.06\n\n\n5\n6\n3\n570\n257.17\n247.13\n\n\n6\n7\n3\n402\n291.77\n185.37\n\n\n7\n8\n3\n567\n301.26\n221.36\n\n\n8\n9\n4\n687\n262.17\n247.09\n\n\n9\n10\n5\n569\n314.28\n198.66\n\n\n10\n11\n5\n965\n298.65\n204.61\n\n\n11\n12\n6\n556\n325.99\n177.05\n\n\n\n\n\n\n\n\n\n\nNow we are going to estimates the average area size under corn and soybeans. To do so, we use the nested error linear regression (special case of the linear mixed model) to model the number of hectares. As auxiliary variables, we use the number of pixel classified as corn and soybeans.\nFirst, we use the method fit() to estimate the model parameters.\n\nareas = countycrop[\"county_id\"]\nys = countycrop[\"corn_area\"]\nXs = countycrop[[\"corn_pixel\", \"soybeans_pixel\"]]\nXp_mean = countycrop_means[[\"ave_corn_pixel\", \"ave_corn_pixel\"]]\nsamp_size = countycrop_means[[\"samp_segments\"]]\npop_size = countycrop_means[[\"pop_segments\"]]\n\nimport numpy as np\nareap = np.linspace(1, 12, 12)\n\n\"\"\"REML Method\"\"\"\neblup_bhf_reml = EblupUnitModel()\neblup_bhf_reml.fit(\n    ys,\n    Xs,\n    areas,\n)\n\neblup_bhf_reml.predict(\n    Xmean=Xp_mean,\n    area=areap,\n)\n\ncorn_est_reml = eblup_bhf_reml.to_dataframe()\n\nprint(corn_est_reml)\n\n   _parameter  _area   _estimate        _mse\n0        mean    1.0  119.357558  139.514787\n1        mean    2.0  120.364916  140.131214\n2        mean    3.0  110.530444  114.058730\n3        mean    4.0  112.879489  113.682072\n4        mean    5.0  133.244364  152.310283\n5        mean    6.0  108.640579   75.256679\n6        mean    7.0  113.284824  121.930105\n7        mean    8.0  120.335415  113.394707\n8        mean    9.0  111.072462   67.357524\n9        mean   10.0  120.669683  118.122346\n10       mean   11.0  109.649316   99.856041\n11       mean   12.0  126.735499  148.884533\n\n\n/Users/msdiallo/Dev/survey-methods/samplics/.venv/lib/python3.13/site-packages/statsmodels/base/optimizer.py:19: FutureWarning: Keyword arguments have been passed to the optimizer that have no effect. The list of allowed keyword arguments for method bfgs is: gtol, norm, epsilon. The list of unsupported keyword arguments passed include: tol. After release 0.14, this will raise.\n  warnings.warn(\n\n\n\n\"\"\"ML Method\"\"\"\neblup_bhf_ml = EblupUnitModel(method=FitMethod.ml)\neblup_bhf_ml.fit(\n    ys,\n    Xs,\n    areas,\n)\n\neblup_bhf_ml.predict(Xp_mean, areap)\n\ncorn_est_ml = eblup_bhf_ml.to_dataframe()\n\nprint(corn_est_ml)\n\n   _parameter  _area   _estimate        _mse\n0        mean    1.0  118.987340  117.381060\n1        mean    2.0  120.091296  119.200583\n2        mean    3.0  111.315363   96.845753\n3        mean    4.0  113.302456   99.756880\n4        mean    5.0  132.143329  138.775182\n5        mean    6.0  108.072837   67.896678\n6        mean    7.0  113.637097  108.711569\n7        mean    8.0  120.189563  103.560647\n8        mean    9.0  110.480483   62.797278\n9        mean   10.0  120.961246  111.594667\n10       mean   11.0  110.577742   93.776110\n11       mean   12.0  126.790379  139.817391\n\n\n\n\n\nAs shown above, the predict() method provides the taylor-based MSE estimates. However, we can also calculate MSE estimates using the bootstrap approach.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Small Area Estimation",
      "Unit Level Modeling"
    ]
  },
  {
    "objectID": "pages/sae_unit.html#county-crop-corn-and-soybeans-areas-data",
    "href": "pages/sae_unit.html#county-crop-corn-and-soybeans-areas-data",
    "title": "Unit Level Modeling",
    "section": "",
    "text": "For this example, we use the county crop data used by Battese, Harter, and Fuller (1988). The datasets contains 37 observations on areas under corn and under soybeans for each of the 12 counties in the north-central of Iowa. Each county was divided in segments and using interviews and LANDSAT satellite data, data on area used for corn and soybeans was obtained. In the unit level data, each observation is a segment with the following variables: county id (county_id), area in hectare under corn (corn_area), area in heactare under soybeans (soybeans_area), number of pixel classified as corn (corn_pixel), and number of pixels classified as soybeans (soybeans_pixel).\n\nfrom samplics.datasets import load_county_crop, load_county_crop_means\nfrom samplics.sae import EblupUnitModel\n\nfrom samplics.utils.types import FitMethod\n\n\n# Load County Crop sample data\ncountycrop_dict = load_county_crop()\ncountycrop = countycrop_dict[\"data\"]\n\nprint(\"First observations from the unit (segment) level crop areas data\")\ncountycrop.head(15)\n\nFirst observations from the unit (segment) level crop areas data\n\n\n\n\n\n\n\n\n\ncounty_id\ncorn_area\nsoybeans_area\ncorn_pixel\nsoybeans_pixel\n\n\n\n\n0\n1\n165.76\n8.09\n374\n55\n\n\n1\n2\n96.32\n106.03\n209\n218\n\n\n2\n3\n76.08\n103.60\n253\n250\n\n\n3\n4\n185.35\n6.47\n432\n96\n\n\n4\n4\n116.43\n63.82\n367\n178\n\n\n5\n5\n162.08\n43.50\n361\n137\n\n\n6\n5\n152.04\n71.43\n288\n206\n\n\n7\n5\n161.75\n42.49\n369\n165\n\n\n8\n6\n92.88\n105.26\n206\n218\n\n\n9\n6\n149.94\n76.49\n316\n221\n\n\n10\n6\n64.75\n174.34\n145\n338\n\n\n11\n7\n127.07\n95.67\n355\n128\n\n\n12\n7\n133.55\n76.57\n295\n147\n\n\n13\n7\n77.70\n93.48\n223\n204\n\n\n14\n8\n206.39\n37.84\n459\n77\n\n\n\n\n\n\n\nIn addition to the unit (segment) level data, we have the small area (county) level averages of the number of pixels classified as corn or soybeans.\n\n# Load County Crop Area Means sample data\ncountycropmeans_dict = load_county_crop_means()\ncountycrop_means = countycropmeans_dict[\"data\"]\n\nprint(f\"County level crop areas averages\")\ncountycrop_means.head(15)\n\nCounty level crop areas averages\n\n\n\n\n\n\n\n\n\ncounty_id\nsamp_segments\npop_segments\nave_corn_pixel\nave_soybeans_pixel\n\n\n\n\n0\n1\n1\n545\n295.29\n189.70\n\n\n1\n2\n1\n566\n300.40\n196.65\n\n\n2\n3\n1\n394\n289.60\n205.28\n\n\n3\n4\n2\n424\n290.74\n220.22\n\n\n4\n5\n3\n564\n318.21\n188.06\n\n\n5\n6\n3\n570\n257.17\n247.13\n\n\n6\n7\n3\n402\n291.77\n185.37\n\n\n7\n8\n3\n567\n301.26\n221.36\n\n\n8\n9\n4\n687\n262.17\n247.09\n\n\n9\n10\n5\n569\n314.28\n198.66\n\n\n10\n11\n5\n965\n298.65\n204.61\n\n\n11\n12\n6\n556\n325.99\n177.05",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Small Area Estimation",
      "Unit Level Modeling"
    ]
  },
  {
    "objectID": "pages/sae_unit.html#empirical-bayes-linear-unbiased-predictor-eblup",
    "href": "pages/sae_unit.html#empirical-bayes-linear-unbiased-predictor-eblup",
    "title": "Unit Level Modeling",
    "section": "",
    "text": "Now we are going to estimates the average area size under corn and soybeans. To do so, we use the nested error linear regression (special case of the linear mixed model) to model the number of hectares. As auxiliary variables, we use the number of pixel classified as corn and soybeans.\nFirst, we use the method fit() to estimate the model parameters.\n\nareas = countycrop[\"county_id\"]\nys = countycrop[\"corn_area\"]\nXs = countycrop[[\"corn_pixel\", \"soybeans_pixel\"]]\nXp_mean = countycrop_means[[\"ave_corn_pixel\", \"ave_corn_pixel\"]]\nsamp_size = countycrop_means[[\"samp_segments\"]]\npop_size = countycrop_means[[\"pop_segments\"]]\n\nimport numpy as np\nareap = np.linspace(1, 12, 12)\n\n\"\"\"REML Method\"\"\"\neblup_bhf_reml = EblupUnitModel()\neblup_bhf_reml.fit(\n    ys,\n    Xs,\n    areas,\n)\n\neblup_bhf_reml.predict(\n    Xmean=Xp_mean,\n    area=areap,\n)\n\ncorn_est_reml = eblup_bhf_reml.to_dataframe()\n\nprint(corn_est_reml)\n\n   _parameter  _area   _estimate        _mse\n0        mean    1.0  119.357558  139.514787\n1        mean    2.0  120.364916  140.131214\n2        mean    3.0  110.530444  114.058730\n3        mean    4.0  112.879489  113.682072\n4        mean    5.0  133.244364  152.310283\n5        mean    6.0  108.640579   75.256679\n6        mean    7.0  113.284824  121.930105\n7        mean    8.0  120.335415  113.394707\n8        mean    9.0  111.072462   67.357524\n9        mean   10.0  120.669683  118.122346\n10       mean   11.0  109.649316   99.856041\n11       mean   12.0  126.735499  148.884533\n\n\n/Users/msdiallo/Dev/survey-methods/samplics/.venv/lib/python3.13/site-packages/statsmodels/base/optimizer.py:19: FutureWarning: Keyword arguments have been passed to the optimizer that have no effect. The list of allowed keyword arguments for method bfgs is: gtol, norm, epsilon. The list of unsupported keyword arguments passed include: tol. After release 0.14, this will raise.\n  warnings.warn(\n\n\n\n\"\"\"ML Method\"\"\"\neblup_bhf_ml = EblupUnitModel(method=FitMethod.ml)\neblup_bhf_ml.fit(\n    ys,\n    Xs,\n    areas,\n)\n\neblup_bhf_ml.predict(Xp_mean, areap)\n\ncorn_est_ml = eblup_bhf_ml.to_dataframe()\n\nprint(corn_est_ml)\n\n   _parameter  _area   _estimate        _mse\n0        mean    1.0  118.987340  117.381060\n1        mean    2.0  120.091296  119.200583\n2        mean    3.0  111.315363   96.845753\n3        mean    4.0  113.302456   99.756880\n4        mean    5.0  132.143329  138.775182\n5        mean    6.0  108.072837   67.896678\n6        mean    7.0  113.637097  108.711569\n7        mean    8.0  120.189563  103.560647\n8        mean    9.0  110.480483   62.797278\n9        mean   10.0  120.961246  111.594667\n10       mean   11.0  110.577742   93.776110\n11       mean   12.0  126.790379  139.817391",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Small Area Estimation",
      "Unit Level Modeling"
    ]
  },
  {
    "objectID": "pages/sae_unit.html#bootstrap-mse-estimation",
    "href": "pages/sae_unit.html#bootstrap-mse-estimation",
    "title": "Unit Level Modeling",
    "section": "",
    "text": "As shown above, the predict() method provides the taylor-based MSE estimates. However, we can also calculate MSE estimates using the bootstrap approach.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Small Area Estimation",
      "Unit Level Modeling"
    ]
  },
  {
    "objectID": "pages/selection_ssus.html",
    "href": "pages/selection_ssus.html",
    "title": "Selection of SSUs",
    "section": "",
    "text": "To select the second stage sample, we need the second stage frame which is the list of all the households in the 10 selected clusters (psus). DHS, PHIA, MICS and other large scale surveys visit the selected clusters and construct the list of all households in the selected clusters.\nBefore starting the second stage selection, let us import the data from the first stage sampling information that is the first stage sample (psu_sample). For clarity, we explicitly import the packages and modules needed for this notebook.\n\nimport numpy as np \nimport pandas as pd \n\nfrom samplics import SelectMethod\nfrom samplics.sampling import SampleSelection\n\n# Load the selected PSUs \npsu_frame = pd.read_csv(\"./psu_frame.csv\")\n\nIn this tutorial, we will simulate the second stage frame. For the simulation, assume that the psu frame was obtained from a previous census conducted several years before. We also assume that, the change in the number of households since the previous census follows a normal distribution with a mean equal to 5% higher than the census value and a variance of 0.15 times the number of households from the census. Under these assumptions, we generate the following second stage frame of households. Note that the frame is created only for the selected PSUs.\n\n# Create a synthetic second stage frame\ncensus_size = psu_frame.loc[\n    psu_frame[\"psu_sample\"] == 1, \n    \"number_households_census\"\n].values\nstratum_names = psu_frame.loc[\n    psu_frame[\"psu_sample\"] == 1, \n    \"region\"\n    ].values\ncluster = psu_frame.loc[psu_frame[\"psu_sample\"] == 1, \"cluster\"].values\n\nnp.random.seed(15)\n\nlisting_size = np.zeros(census_size.size)\nfor k in range(census_size.size):\n    listing_size[k] = np.random.normal(\n        1.05 * census_size[k], 0.15 * census_size[k]\n        )\n\nlisting_size = listing_size.astype(int)\nhh_id = rr_id = cl_id = []\nfor k, s in enumerate(listing_size):\n    hh_k1 = np.char.array(np.repeat(stratum_names[k], s)).astype(str)\n    hh_k2 = np.char.array(np.arange(1, s + 1)).astype(str)\n    cl_k = np.repeat(cluster[k], s)\n    hh_k = np.char.add(np.char.array(cl_k).astype(str), hh_k2)\n    hh_id = np.append(hh_id, hh_k)\n    rr_id = np.append(rr_id, hh_k1)\n    cl_id = np.append(cl_id, cl_k)\n\nssu_frame = pd.DataFrame(cl_id.astype(int))\nssu_frame.rename(columns={0: \"cluster\"}, inplace=True)\nssu_frame[\"region\"] = rr_id\nssu_frame[\"household\"] = hh_id\n\nnb_obs = 15\nprint(f\"\\nFirst {nb_obs} observations of the SSU frame\\n\")\nssu_frame.head(nb_obs)\n\n\nFirst 15 observations of the SSU frame\n\n\n\n\n\n\n\n\n\n\ncluster\nregion\nhousehold\n\n\n\n\n0\n7\nNorth\n71\n\n\n1\n7\nNorth\n72\n\n\n2\n7\nNorth\n73\n\n\n3\n7\nNorth\n74\n\n\n4\n7\nNorth\n75\n\n\n5\n7\nNorth\n76\n\n\n6\n7\nNorth\n77\n\n\n7\n7\nNorth\n78\n\n\n8\n7\nNorth\n79\n\n\n9\n7\nNorth\n710\n\n\n10\n7\nNorth\n711\n\n\n11\n7\nNorth\n712\n\n\n12\n7\nNorth\n713\n\n\n13\n7\nNorth\n714\n\n\n14\n7\nNorth\n715\n\n\n\n\n\n\n\n\npsu_sample = psu_frame.loc[psu_frame[\"psu_sample\"] == 1]\nssu_counts = ssu_frame.groupby(\"cluster\").count()\nssu_counts.drop(columns=\"region\", inplace=True)\nssu_counts.reset_index(inplace=True)\nssu_counts.rename(\n    columns={\"household\": \"number_households_listed\"}, \n    inplace=True\n    )\n\npd.merge(\n    psu_sample[[\"cluster\", \"region\", \"number_households_census\"]],\n    ssu_counts[[\"cluster\", \"number_households_listed\"]],\n    on=[\"cluster\"],\n)\n\n\n\n\n\n\n\n\ncluster\nregion\nnumber_households_census\nnumber_households_listed\n\n\n\n\n0\n7\nNorth\n130\n130\n\n\n1\n10\nNorth\n600\n660\n\n\n2\n16\nSouth\n190\n195\n\n\n3\n24\nSouth\n75\n73\n\n\n4\n29\nSouth\n200\n217\n\n\n5\n34\nEast\n305\n239\n\n\n6\n45\nEast\n450\n398\n\n\n7\n52\nEast\n700\n620\n\n\n8\n64\nWest\n300\n301\n\n\n9\n86\nWest\n280\n274\n\n\n\n\n\n\n\nAccording to the simulated second stage frame, we get the same number of households in cluster 7 as the census. However, in strata 10, 16, 29, and 64, we listed more households than during than the census. And finally, we found less households in the remaining clusters than the census.\nNow that we have a second stage frame, let’s use samplics to calculate the probabilities of selection and to select a sample. The second stage sample size is 150 households and the strategy is to select 15 households per cluster.\n\nSSU (household) Probability of Selection\nThe second stage probabilities of selection are conditional on the first stage realization. For this stage, simple random selection (srs) and systematic selection(sys) are common methods used to select households. For this example, we use srs to select 15 households from each cluster. Conditionally to teh first stage, the second stage selection is a stratified srs where the clusters are the strata. More generally, we have that \\[\\begin{equation} p_{hij} = \\frac{m_{hi}}{M_{hi}^{'}} \\end{equation}\\] where \\(p_{hij}\\) is the conditional probability of selection for unit \\(j\\) from stratum \\(h\\) and cluster \\(j\\), \\(m_{hi}\\) and \\(M_{hi}^{'}\\) are the sample size and the number of secondary sampling units listed for stratum \\(h\\) and cluster \\(j\\), respectively.\nIn this scenario, sample size is the same in each stratum. Hence, the parameter sample_size does not need to be a Python dictionary; we will only provide 15 in the function call.\n\nstage2_design = SampleSelection(\n    method=SelectMethod.srs_wor, strat=True, wr=False\n)\n\nssu_frame[\"ssu_prob\"] = stage2_design.inclusion_probs(\n    ssu_frame[\"household\"], 15, ssu_frame[\"cluster\"]\n)\n\nssu_frame.sample(20)\n\n\n\n\n\n\n\n\ncluster\nregion\nhousehold\nssu_prob\n\n\n\n\n1438\n34\nEast\n34164\n0.004828\n\n\n2517\n52\nEast\n52606\n0.004828\n\n\n2943\n86\nWest\n86111\n0.004828\n\n\n3002\n86\nWest\n86170\n0.004828\n\n\n559\n10\nNorth\n10430\n0.004828\n\n\n1216\n29\nSouth\n29159\n0.004828\n\n\n2751\n64\nWest\n64220\n0.004828\n\n\n412\n10\nNorth\n10283\n0.004828\n\n\n2549\n64\nWest\n6418\n0.004828\n\n\n1508\n34\nEast\n34234\n0.004828\n\n\n1517\n45\nEast\n454\n0.004828\n\n\n2658\n64\nWest\n64127\n0.004828\n\n\n1072\n29\nSouth\n2915\n0.004828\n\n\n1074\n29\nSouth\n2917\n0.004828\n\n\n1669\n45\nEast\n45156\n0.004828\n\n\n1286\n34\nEast\n3412\n0.004828\n\n\n1162\n29\nSouth\n29105\n0.004828\n\n\n1051\n24\nSouth\n2467\n0.004828\n\n\n3085\n86\nWest\n86253\n0.004828\n\n\n2148\n52\nEast\n52237\n0.004828\n\n\n\n\n\n\n\n\nSSU (household) Selection\nThe second stage sample is selected from the SSU frame (ssu_frame) using the variable cluster as the strat variable. The sample is selected without replacement according to the specification of the second stage design. Hence, both ssu_sample and ssu_hits sum to 150 and each selected household was hit only ounce (i.e. ssu_hits = 1).\n\nnp.random.seed(11)\nssu_sample, ssu_hits, ssu_probs = stage2_design.select(\n    ssu_frame[\"household\"], 15, ssu_frame[\"cluster\"]\n)\n\nssu_frame[\"ssu_sample\"] = ssu_sample\nssu_frame[\"ssu_hits\"] = ssu_hits\nssu_frame[\"ssu_probs\"] = ssu_probs\n\nssu_frame[ssu_frame[\"ssu_sample\"] == 1].sample(15)\n\n\n\n\n\n\n\n\ncluster\nregion\nhousehold\nssu_prob\nssu_sample\nssu_hits\nssu_probs\n\n\n\n\n2319\n52\nEast\n52408\n0.004828\nTrue\n1\n0.024194\n\n\n2931\n86\nWest\n8699\n0.004828\nTrue\n1\n0.054745\n\n\n2642\n64\nWest\n64111\n0.004828\nTrue\n1\n0.049834\n\n\n122\n7\nNorth\n7123\n0.004828\nTrue\n1\n0.115385\n\n\n60\n7\nNorth\n761\n0.004828\nTrue\n1\n0.115385\n\n\n945\n16\nSouth\n16156\n0.004828\nTrue\n1\n0.076923\n\n\n338\n10\nNorth\n10209\n0.004828\nTrue\n1\n0.022727\n\n\n2218\n52\nEast\n52307\n0.004828\nTrue\n1\n0.024194\n\n\n2870\n86\nWest\n8638\n0.004828\nTrue\n1\n0.054745\n\n\n1764\n45\nEast\n45251\n0.004828\nTrue\n1\n0.037688\n\n\n630\n10\nNorth\n10501\n0.004828\nTrue\n1\n0.022727\n\n\n1441\n34\nEast\n34167\n0.004828\nTrue\n1\n0.062762\n\n\n986\n24\nSouth\n242\n0.004828\nTrue\n1\n0.205479\n\n\n1796\n45\nEast\n45283\n0.004828\nTrue\n1\n0.037688\n\n\n1264\n29\nSouth\n29207\n0.004828\nTrue\n1\n0.069124\n\n\n\n\n\n\n\nLet’s check that both ssu_sample and ssu_hits sum to 150 and each selected household was hit only ounce (i.e. ssu_hits = 1).\n\n\nprint(f\"The sum of `ssu_sample` is equal to: {ssu_frame['ssu_sample'].sum()}\\n\")\n\nThe sum of `ssu_sample` is equal to: 150\n\n\n\n\nprint(f\"The sum of `ssu_hits` is equal to: {ssu_frame['ssu_hits'].sum()}\\n\")\n\nThe sum of `ssu_hits` is equal to: 150\n\n\n\n\nprint(f\"The values of `ssu_hits` are: {np.unique(ssu_frame['ssu_hits']).tolist()}\\n\")\n\nThe values of `ssu_hits` are: [0, 1]\n\n\n\n\nTo use systematic selection, we just need to replace method=SelectMethod.srs_wor by method=SelectMethod.sys.\nAnother common approach is to use a rate for selecting the sample. Instead of selecting 15 households from 130 in the first cluster, we may want to select with a rate of 15/130, and similarly for the other clusters.\n\nrates = np.repeat(15, 10) / ssu_counts[\"number_households_listed\"].values\nssu_rates = dict(zip(np.unique(ssu_frame[\"cluster\"]), rates))\nssu_rates\n\n{np.int64(7): np.float64(0.11538461538461539),\n np.int64(10): np.float64(0.022727272727272728),\n np.int64(16): np.float64(0.07692307692307693),\n np.int64(24): np.float64(0.2054794520547945),\n np.int64(29): np.float64(0.06912442396313365),\n np.int64(34): np.float64(0.06276150627615062),\n np.int64(45): np.float64(0.03768844221105527),\n np.int64(52): np.float64(0.024193548387096774),\n np.int64(64): np.float64(0.04983388704318937),\n np.int64(86): np.float64(0.05474452554744526)}\n\n\n\nnp.random.seed(22)\n\nstage2_design2 = SampleSelection(\n    method=SelectMethod.sys, strat=True, wr=False\n)\n\nssu_sample_r, ssu_hits_r, _ = stage2_design2.select(\n    ssu_frame[\"household\"], \n    stratum=ssu_frame[\"cluster\"], \n    samp_rate=ssu_rates\n)\n\nssu_sample2 = pd.DataFrame(\n    data={\n        \"household\": ssu_frame[\"household\"],\n        \"ssu_sample_r\": ssu_sample_r,\n        \"ssu_hits_r\": ssu_hits_r,\n    }\n)\n\nssu_sample2.head(25)\n\n\n\n\n\n\n\n\nhousehold\nssu_sample_r\nssu_hits_r\n\n\n\n\n0\n71\n0\n0\n\n\n1\n72\n0\n0\n\n\n2\n73\n0\n0\n\n\n3\n74\n0\n0\n\n\n4\n75\n0\n0\n\n\n5\n76\n1\n1\n\n\n6\n77\n0\n0\n\n\n7\n78\n0\n0\n\n\n8\n79\n0\n0\n\n\n9\n710\n0\n0\n\n\n10\n711\n0\n0\n\n\n11\n712\n0\n0\n\n\n12\n713\n0\n0\n\n\n13\n714\n1\n1\n\n\n14\n715\n0\n0\n\n\n15\n716\n0\n0\n\n\n16\n717\n0\n0\n\n\n17\n718\n0\n0\n\n\n18\n719\n0\n0\n\n\n19\n720\n0\n0\n\n\n20\n721\n0\n0\n\n\n21\n722\n1\n1\n\n\n22\n723\n0\n0\n\n\n23\n724\n0\n0\n\n\n24\n725\n0\n0\n\n\n\n\n\n\n\nLet’s store the first and second stages samples.\n\n# First stage sample\npsu_sample[[\"cluster\", \"region\", \"psu_prob\"]].to_csv(\"psu_sample.csv\")\n\n# Second stage sample\nssu_sample = ssu_frame.loc[ssu_frame[\"ssu_sample\"] == 1]\nssu_sample[[\"cluster\", \"household\", \"ssu_prob\"]].to_csv(\"ssu_sample.csv\")",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Selection",
      "Selection of SSUs"
    ]
  },
  {
    "objectID": "pages/sae.html",
    "href": "pages/sae.html",
    "title": "Overview",
    "section": "",
    "text": "SAE techniques can be considered as extensions of the survey estimation techniques that only use the sample data to produce estimates. Note that we include model assisted techniques such as the Generalized Regression (GREG) estimator in this category. These are often referred to as the direct estimates. In some applications, sample sizes are not large enough to produce reliable direct estimates for the domains of interest. In these situations, one possible way to improve the domain level estimates is to use SAE techniques which require availability of auxiliary information. Depending on the SAE method used, the auxiliary information needs to be available at the domains aggregated level or for non-sampled observations in the target population. SAE techniques produce modeled estimates of parameters of interest. There are two main categories of SAE methods that are the area level and the unit level, discussed in this tutorial.\nSection 1: Area level modeling  Section 2: Unit level modeling \nGeneralized linear mixed model are the statistical framework used to develop the SAE methods, for an introduction to GLMM see McCulloch, Searle, and Neuhaus (2008). For a comprehensive review of the small area estimation models and its applications, see Rao and Molina (2015).\n\n\n\n\nReferences\n\nMcCulloch, C E, S R Searle, and J M Neuhaus. 2008. Generalized, Linear, and Mixed Models. New York: John Wiley; Sons.\n\n\nRao, J. N. K., and I Molina. 2015. Small Area Estimation, 2nd edn. John Wiley & Sons, Hoboken, New Jersey.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Small Area Estimation",
      "Overview"
    ]
  },
  {
    "objectID": "pages/selection.html",
    "href": "pages/selection.html",
    "title": "Overviews",
    "section": "",
    "text": "When a sample frame is available, the simple random sampling (srs) method is usually the easiest way to select a sample. However, operational and cost constraints often result in much more complex mechanisms for selecting samples. Common use cases of complex sampling designs are the national household surveys such as The Demographic and Health Surveys (DHS), The Living Standards Measurement Study (LSMS), The Multiple Indicator Cluster Surveys (MICS), and many more. In the national household surveys, the sample is selected in stages. For example, at the first stage clusters of households are selected using probability proportional to size (pps) method, at the second stage households are selected from the sampled clusters, and at the final stage individuals are selected from households in the sample.\nIn this tutorial, we generate simulated data to illustrate the first and second stage selections of clusters and households, respectively.\nSection 1: Selection of PSUs  Section 2: Selection of SSUs \nFor a comprehensive review of sampling techniques, users may want to consult Brewer and Hanif (1983), Cochran (1977), Kish (1965), and Lohr (2021).\n\n\n\n\nReferences\n\nBrewer, K. R. W., and M Hanif. 1983. Sampling With Unequal Probabilities. Springer-Verlag New York, Inc. https://doi.org/10.1007/9781468494075.\n\n\nCochran, William G. 1977. Sampling Techniques, 3rd edn. John Wiley & Sons, Inc.\n\n\nKish, Leslie. 1965. Survey Sampling. John Wiley & Sons, Inc.\n\n\nLohr, Sharon L. 2021. Sampling: Design and Analysis, Third Edition. Chapman; Hall/CRC. https://doi.org/https://doi.org/10.1201/9780429298899.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Selection",
      "Overviews"
    ]
  },
  {
    "objectID": "pages/weight_replicates.html",
    "href": "pages/weight_replicates.html",
    "title": "Replicate Weights",
    "section": "",
    "text": "Replicate weights are usually created for the purpose of variance (uncertainty) estimation. One common use case for replication-based methods is the estimation of non-linear parameters fow which Taylor-based approximation may not be accurate enough. Another use case is when the number of primary sampling units selected per stratum is small (low degree of freedom). Replicate weights are usually created for the purpose of variance (uncertainty)estimation. One common use case for replication-based methods is the estimation of non-linear parameters fow which Taylor-based approximation may not be accurate enough. Another use case is when the number of primary sampling units selected per stratum is small (low degree of freedom).\nIn this tutorial, we will explore creating replicate weights using the class ReplicateWeight. Three replication methods have been implemented: balanced repeated replication (BRR) including the Fay-BRR, bootstrap and jackknife. The replicate method of interest is specified when initializing the class by using the parameter method. The parameter method takes the values “bootstrap”, “brr”, or “jackknife”. In this tutorial, we show how the API works for producing replicate weights.\n\nimport pandas as pd\n\nfrom samplics.datasets import load_psu_sample, load_ssu_sample\nfrom samplics.weighting import ReplicateWeight\n\nfrom samplics.utils.types import RepMethod\n\nWe import the sample data…\n\n# Load PSU sample data\npsu_sample_dict = load_psu_sample()\npsu_sample = psu_sample_dict[\"data\"]\n\n# Load PSU sample data\nssu_sample_dict = load_ssu_sample()\nssu_sample = ssu_sample_dict[\"data\"]\n\nfull_sample = pd.merge(\n    psu_sample[[\"cluster\", \"region\", \"psu_prob\"]], \n    ssu_sample[[\"cluster\", \"household\", \"ssu_prob\"]], \n    on=\"cluster\")\n\nfull_sample[\"inclusion_prob\"] = \\\n    full_sample[\"psu_prob\"] * full_sample[\"ssu_prob\"] \nfull_sample[\"design_weight\"] = 1 / full_sample[\"inclusion_prob\"] \n\nfull_sample.head(15)\n\n\n\n\n\n\n\n\ncluster\nregion\npsu_prob\nhousehold\nssu_prob\ninclusion_prob\ndesign_weight\n\n\n\n\n0\n7\nNorth\n0.187726\n72\n0.115385\n0.021661\n46.166667\n\n\n1\n7\nNorth\n0.187726\n73\n0.115385\n0.021661\n46.166667\n\n\n2\n7\nNorth\n0.187726\n75\n0.115385\n0.021661\n46.166667\n\n\n3\n7\nNorth\n0.187726\n715\n0.115385\n0.021661\n46.166667\n\n\n4\n7\nNorth\n0.187726\n722\n0.115385\n0.021661\n46.166667\n\n\n5\n7\nNorth\n0.187726\n724\n0.115385\n0.021661\n46.166667\n\n\n6\n7\nNorth\n0.187726\n755\n0.115385\n0.021661\n46.166667\n\n\n7\n7\nNorth\n0.187726\n761\n0.115385\n0.021661\n46.166667\n\n\n8\n7\nNorth\n0.187726\n764\n0.115385\n0.021661\n46.166667\n\n\n9\n7\nNorth\n0.187726\n782\n0.115385\n0.021661\n46.166667\n\n\n10\n7\nNorth\n0.187726\n795\n0.115385\n0.021661\n46.166667\n\n\n11\n7\nNorth\n0.187726\n7111\n0.115385\n0.021661\n46.166667\n\n\n12\n7\nNorth\n0.187726\n7112\n0.115385\n0.021661\n46.166667\n\n\n13\n7\nNorth\n0.187726\n7117\n0.115385\n0.021661\n46.166667\n\n\n14\n7\nNorth\n0.187726\n7123\n0.115385\n0.021661\n46.166667\n\n\n\n\n\n\n\n\nBalanced Repeated Replication (BRR)\nThe basic idea of BRR is to slip the sample in independent random groups. The groups are then threated as independent replicates of the the sample design. A special case is when the sample is split into two half samples in each stratum. This design is suitable to many survey designs where only two psus are selected by stratum. In practice, one of the psu is asigned to the first random group and the other psu is assign to the second group. The sample weights are double for one group (say the first one) and the sample weights in the other group are set to zero. To ensure that the replicates are independent, we use hadamard matrices to assign the random groups.\n\nimport scipy\nscipy.linalg.hadamard(8)\n\narray([[ 1,  1,  1,  1,  1,  1,  1,  1],\n       [ 1, -1,  1, -1,  1, -1,  1, -1],\n       [ 1,  1, -1, -1,  1,  1, -1, -1],\n       [ 1, -1, -1,  1,  1, -1, -1,  1],\n       [ 1,  1,  1,  1, -1, -1, -1, -1],\n       [ 1, -1,  1, -1, -1,  1, -1,  1],\n       [ 1,  1, -1, -1, -1, -1,  1,  1],\n       [ 1, -1, -1,  1, -1,  1,  1, -1]])\n\n\nIn our example, we have 10 psus. If we do not have explicit stratification then replicate() will group the clusters into 5 strata (2 per stratum). In this case, the smallest number of replicates possible using the hadamard matrix is 8.\nThe result below shows that replicate() created 5 strata by grouping clusters 7 and 10 in the first stratum, clusters 16 and 24 in the second stratum, and so on. We can achieve the same result by providing setting strat=True and providing the stratum variable to replicate().\n\nbrr = ReplicateWeight(method=RepMethod.brr, strat=False)\nbrr_wgt = brr.replicate(\n    samp_weight=full_sample[\"design_weight\"], \n    psu=full_sample[\"cluster\"]\n    )\n\nbrr_wgt.drop_duplicates().head(10)\n\n\n\n\n\n\n\n\n_stratum\n_psu\n_samp_weight\n_brr_wgt_1\n_brr_wgt_2\n_brr_wgt_3\n_brr_wgt_4\n_brr_wgt_5\n_brr_wgt_6\n_brr_wgt_7\n_brr_wgt_8\n\n\n\n\n0\n1\n7\n46.166667\n0.000000\n92.333333\n0.000000\n92.333333\n0.000000\n92.333333\n0.000000\n92.333333\n\n\n15\n1\n10\n50.783333\n101.566667\n0.000000\n101.566667\n0.000000\n101.566667\n0.000000\n101.566667\n0.000000\n\n\n30\n2\n16\n62.149123\n0.000000\n0.000000\n124.298246\n124.298246\n0.000000\n0.000000\n124.298246\n124.298246\n\n\n45\n2\n24\n58.940741\n117.881481\n117.881481\n0.000000\n0.000000\n117.881481\n117.881481\n0.000000\n0.000000\n\n\n60\n3\n29\n65.702778\n0.000000\n131.405556\n131.405556\n0.000000\n0.000000\n131.405556\n131.405556\n0.000000\n\n\n75\n3\n34\n75.661566\n151.323133\n0.000000\n0.000000\n151.323133\n151.323133\n0.000000\n0.000000\n151.323133\n\n\n90\n4\n45\n85.398025\n0.000000\n0.000000\n0.000000\n0.000000\n170.796049\n170.796049\n170.796049\n170.796049\n\n\n105\n4\n52\n85.520635\n171.041270\n171.041270\n171.041270\n171.041270\n0.000000\n0.000000\n0.000000\n0.000000\n\n\n120\n5\n64\n218.893889\n0.000000\n437.787778\n0.000000\n437.787778\n437.787778\n0.000000\n437.787778\n0.000000\n\n\n135\n5\n86\n213.491667\n426.983333\n0.000000\n426.983333\n0.000000\n0.000000\n426.983333\n0.000000\n426.983333\n\n\n\n\n\n\n\nAn extension of BRR is the Fay’s method. In the Fay’s approach, instead of multiplying one half-sample by zero, we multiple the sampel weights by a factor \\(\\alpha\\) and the other halh-sample by \\(2-\\alpha\\). We refer to \\(\\alpha\\) as the fay coefficient. Note that when \\(\\alpha=0\\) then teh Fay’s method reduces to BRR.\n\nfay = ReplicateWeight(method=RepMethod.brr, strat=False, fay_coef=0.3)\nfay_wgt = fay.replicate(\n    samp_weight=full_sample[\"design_weight\"], \n    psu=full_sample[\"cluster\"], \n    rep_prefix=\"fay_weight_\",\n    psu_varname=\"cluster\", \n    str_varname=\"stratum\"\n)\n\nfay_wgt.drop_duplicates().head(10)\n\n\n\n\n\n\n\n\nstratum\ncluster\n_samp_weight\nfay_weight_1\nfay_weight_2\nfay_weight_3\nfay_weight_4\nfay_weight_5\nfay_weight_6\nfay_weight_7\nfay_weight_8\n\n\n\n\n0\n1\n7\n46.166667\n13.850000\n78.483333\n13.850000\n78.483333\n13.850000\n78.483333\n13.850000\n78.483333\n\n\n15\n1\n10\n50.783333\n86.331667\n15.235000\n86.331667\n15.235000\n86.331667\n15.235000\n86.331667\n15.235000\n\n\n30\n2\n16\n62.149123\n18.644737\n18.644737\n105.653509\n105.653509\n18.644737\n18.644737\n105.653509\n105.653509\n\n\n45\n2\n24\n58.940741\n100.199259\n100.199259\n17.682222\n17.682222\n100.199259\n100.199259\n17.682222\n17.682222\n\n\n60\n3\n29\n65.702778\n19.710833\n111.694722\n111.694722\n19.710833\n19.710833\n111.694722\n111.694722\n19.710833\n\n\n75\n3\n34\n75.661566\n128.624663\n22.698470\n22.698470\n128.624663\n128.624663\n22.698470\n22.698470\n128.624663\n\n\n90\n4\n45\n85.398025\n25.619407\n25.619407\n25.619407\n25.619407\n145.176642\n145.176642\n145.176642\n145.176642\n\n\n105\n4\n52\n85.520635\n145.385079\n145.385079\n145.385079\n145.385079\n25.656190\n25.656190\n25.656190\n25.656190\n\n\n120\n5\n64\n218.893889\n65.668167\n372.119611\n65.668167\n372.119611\n372.119611\n65.668167\n372.119611\n65.668167\n\n\n135\n5\n86\n213.491667\n362.935833\n64.047500\n362.935833\n64.047500\n64.047500\n362.935833\n64.047500\n362.935833\n\n\n\n\n\n\n\n\n\n\n\n\n\nImportant\n\n\n\nFor any of the three methods, we can request the replicate coefficient instead of the replicate weights by using rep_coefs=True.\n\n\n\n#fay = ReplicateWeight(method=\"brr\", strat=False, fay_coef=0.3)\nfay_wgt = fay.replicate(\n    samp_weight=full_sample[\"design_weight\"], \n    psu=full_sample[\"cluster\"], \n    rep_prefix=\"fay_weight_\",\n    psu_varname=\"cluster\", \n    str_varname=\"stratum\",\n    rep_coefs=True\n)\n\nfay_wgt.drop_duplicates().head(10)\n\n\n\n\n\n\n\n\nstratum\ncluster\n_samp_weight\nfay_weight_1\nfay_weight_2\nfay_weight_3\nfay_weight_4\nfay_weight_5\nfay_weight_6\nfay_weight_7\nfay_weight_8\n\n\n\n\n0\n1\n7\n46.166667\n0.3\n1.7\n0.3\n1.7\n0.3\n1.7\n0.3\n1.7\n\n\n15\n1\n10\n50.783333\n1.7\n0.3\n1.7\n0.3\n1.7\n0.3\n1.7\n0.3\n\n\n30\n2\n16\n62.149123\n0.3\n0.3\n1.7\n1.7\n0.3\n0.3\n1.7\n1.7\n\n\n45\n2\n24\n58.940741\n1.7\n1.7\n0.3\n0.3\n1.7\n1.7\n0.3\n0.3\n\n\n60\n3\n29\n65.702778\n0.3\n1.7\n1.7\n0.3\n0.3\n1.7\n1.7\n0.3\n\n\n75\n3\n34\n75.661566\n1.7\n0.3\n0.3\n1.7\n1.7\n0.3\n0.3\n1.7\n\n\n90\n4\n45\n85.398025\n0.3\n0.3\n0.3\n0.3\n1.7\n1.7\n1.7\n1.7\n\n\n105\n4\n52\n85.520635\n1.7\n1.7\n1.7\n1.7\n0.3\n0.3\n0.3\n0.3\n\n\n120\n5\n64\n218.893889\n0.3\n1.7\n0.3\n1.7\n1.7\n0.3\n1.7\n0.3\n\n\n135\n5\n86\n213.491667\n1.7\n0.3\n1.7\n0.3\n0.3\n1.7\n0.3\n1.7\n\n\n\n\n\n\n\n\n\nBootstrap\nFor the bootstrap replicates, we need to provide the number of replicates. When the number of replicates is not provided, ReplicateWeight will default to 500. The bootstrap consists of selecting the same number of psus as in the sample but with replacement. The selection is independently repeated for each replicate.\n\nbootstrap = ReplicateWeight(\n    method=RepMethod.bootstrap, \n    strat=False, \n    nb_reps=50\n    )\nboot_wgt = bootstrap.replicate(\n    samp_weight=full_sample[\"design_weight\"], \n    psu=full_sample[\"cluster\"]\n    )\n\nboot_wgt.drop_duplicates().head(10)\n\n\n\n\n\n\n\n\n_psu\n_samp_weight\n_boot_wgt_1\n_boot_wgt_2\n_boot_wgt_3\n_boot_wgt_4\n_boot_wgt_5\n_boot_wgt_6\n_boot_wgt_7\n_boot_wgt_8\n...\n_boot_wgt_41\n_boot_wgt_42\n_boot_wgt_43\n_boot_wgt_44\n_boot_wgt_45\n_boot_wgt_46\n_boot_wgt_47\n_boot_wgt_48\n_boot_wgt_49\n_boot_wgt_50\n\n\n\n\n0\n7\n46.166667\n51.296296\n51.296296\n51.296296\n51.296296\n0.000000\n102.592593\n51.296296\n0.000000\n...\n0.000000\n0.000000\n0.000000\n0.000000\n51.296296\n102.592593\n51.296296\n102.592593\n0.000000\n0.000000\n\n\n15\n10\n50.783333\n0.000000\n0.000000\n56.425926\n112.851852\n112.851852\n56.425926\n56.425926\n112.851852\n...\n0.000000\n56.425926\n0.000000\n56.425926\n56.425926\n0.000000\n56.425926\n56.425926\n225.703704\n56.425926\n\n\n30\n16\n62.149123\n138.109162\n69.054581\n138.109162\n69.054581\n69.054581\n69.054581\n69.054581\n138.109162\n...\n69.054581\n69.054581\n69.054581\n0.000000\n0.000000\n69.054581\n69.054581\n69.054581\n0.000000\n69.054581\n\n\n45\n24\n58.940741\n0.000000\n0.000000\n0.000000\n0.000000\n0.000000\n0.000000\n65.489712\n65.489712\n...\n65.489712\n196.469136\n130.979424\n65.489712\n196.469136\n0.000000\n196.469136\n65.489712\n0.000000\n65.489712\n\n\n60\n29\n65.702778\n0.000000\n219.009259\n73.003086\n73.003086\n0.000000\n0.000000\n73.003086\n73.003086\n...\n0.000000\n0.000000\n73.003086\n73.003086\n73.003086\n73.003086\n73.003086\n0.000000\n73.003086\n73.003086\n\n\n75\n34\n75.661566\n84.068407\n0.000000\n168.136814\n84.068407\n0.000000\n84.068407\n168.136814\n0.000000\n...\n0.000000\n168.136814\n84.068407\n84.068407\n0.000000\n84.068407\n0.000000\n0.000000\n84.068407\n0.000000\n\n\n90\n45\n85.398025\n94.886694\n189.773388\n94.886694\n94.886694\n189.773388\n94.886694\n0.000000\n0.000000\n...\n189.773388\n0.000000\n94.886694\n189.773388\n0.000000\n94.886694\n0.000000\n0.000000\n189.773388\n94.886694\n\n\n105\n52\n85.520635\n95.022928\n95.022928\n0.000000\n0.000000\n95.022928\n0.000000\n0.000000\n0.000000\n...\n95.022928\n0.000000\n95.022928\n95.022928\n0.000000\n95.022928\n0.000000\n190.045855\n95.022928\n190.045855\n\n\n120\n64\n218.893889\n243.215432\n0.000000\n0.000000\n486.430864\n0.000000\n243.215432\n243.215432\n729.646296\n...\n486.430864\n243.215432\n486.430864\n243.215432\n243.215432\n486.430864\n243.215432\n243.215432\n0.000000\n0.000000\n\n\n135\n86\n213.491667\n474.425926\n237.212963\n237.212963\n0.000000\n711.638889\n474.425926\n237.212963\n0.000000\n...\n474.425926\n237.212963\n0.000000\n237.212963\n474.425926\n0.000000\n237.212963\n237.212963\n0.000000\n474.425926\n\n\n\n\n10 rows × 52 columns\n\n\n\n\n\nJackknife\nBelow, we illustrate the API for creating replicate weights using the jackknife method.\n\njackknife = ReplicateWeight(\n    method=RepMethod.jackknife, \n    strat=False\n    )\njkn_wgt = jackknife.replicate(\n    samp_weight=full_sample[\"design_weight\"], \n    psu=full_sample[\"cluster\"]\n    )\n\njkn_wgt.drop_duplicates().head(10)\n\n\n\n\n\n\n\n\n_psu\n_samp_weight\n_jk_wgt_1\n_jk_wgt_2\n_jk_wgt_3\n_jk_wgt_4\n_jk_wgt_5\n_jk_wgt_6\n_jk_wgt_7\n_jk_wgt_8\n_jk_wgt_9\n_jk_wgt_10\n\n\n\n\n0\n7\n46.166667\n0.000000\n51.296296\n51.296296\n51.296296\n51.296296\n51.296296\n51.296296\n51.296296\n51.296296\n51.296296\n\n\n15\n10\n50.783333\n56.425926\n0.000000\n56.425926\n56.425926\n56.425926\n56.425926\n56.425926\n56.425926\n56.425926\n56.425926\n\n\n30\n16\n62.149123\n69.054581\n69.054581\n0.000000\n69.054581\n69.054581\n69.054581\n69.054581\n69.054581\n69.054581\n69.054581\n\n\n45\n24\n58.940741\n65.489712\n65.489712\n65.489712\n0.000000\n65.489712\n65.489712\n65.489712\n65.489712\n65.489712\n65.489712\n\n\n60\n29\n65.702778\n73.003086\n73.003086\n73.003086\n73.003086\n0.000000\n73.003086\n73.003086\n73.003086\n73.003086\n73.003086\n\n\n75\n34\n75.661566\n84.068407\n84.068407\n84.068407\n84.068407\n84.068407\n0.000000\n84.068407\n84.068407\n84.068407\n84.068407\n\n\n90\n45\n85.398025\n94.886694\n94.886694\n94.886694\n94.886694\n94.886694\n94.886694\n0.000000\n94.886694\n94.886694\n94.886694\n\n\n105\n52\n85.520635\n95.022928\n95.022928\n95.022928\n95.022928\n95.022928\n95.022928\n95.022928\n0.000000\n95.022928\n95.022928\n\n\n120\n64\n218.893889\n243.215432\n243.215432\n243.215432\n243.215432\n243.215432\n243.215432\n243.215432\n243.215432\n0.000000\n243.215432\n\n\n135\n86\n213.491667\n237.212963\n237.212963\n237.212963\n237.212963\n237.212963\n237.212963\n237.212963\n237.212963\n237.212963\n0.000000\n\n\n\n\n\n\n\nWith stratification…\n\njackknife = ReplicateWeight(method=RepMethod.jackknife, strat=True)\njkn_wgt = jackknife.replicate(\n    samp_weight=full_sample[\"design_weight\"], \n    psu=full_sample[\"cluster\"], \n    stratum=full_sample[\"region\"]\n    )\n\njkn_wgt.drop_duplicates().head(10)\n\n\n\n\n\n\n\n\n_stratum\n_psu\n_samp_weight\n_jk_wgt_1\n_jk_wgt_2\n_jk_wgt_3\n_jk_wgt_4\n_jk_wgt_5\n_jk_wgt_6\n_jk_wgt_7\n_jk_wgt_8\n_jk_wgt_9\n_jk_wgt_10\n\n\n\n\n0\nEast\n52\n85.520635\n0.000000\n69.250000\n69.250000\n46.166667\n46.166667\n46.166667\n46.166667\n46.166667\n46.166667\n46.166667\n\n\n1\nEast\n45\n85.398025\n69.250000\n0.000000\n69.250000\n46.166667\n46.166667\n46.166667\n46.166667\n46.166667\n46.166667\n46.166667\n\n\n15\nEast\n52\n85.520635\n0.000000\n76.175000\n76.175000\n50.783333\n50.783333\n50.783333\n50.783333\n50.783333\n50.783333\n50.783333\n\n\n16\nEast\n45\n85.398025\n76.175000\n0.000000\n76.175000\n50.783333\n50.783333\n50.783333\n50.783333\n50.783333\n50.783333\n50.783333\n\n\n20\nEast\n34\n75.661566\n76.175000\n76.175000\n0.000000\n50.783333\n50.783333\n50.783333\n50.783333\n50.783333\n50.783333\n50.783333\n\n\n30\nEast\n34\n75.661566\n93.223684\n93.223684\n0.000000\n62.149123\n62.149123\n62.149123\n62.149123\n62.149123\n62.149123\n62.149123\n\n\n33\nEast\n45\n85.398025\n93.223684\n0.000000\n93.223684\n62.149123\n62.149123\n62.149123\n62.149123\n62.149123\n62.149123\n62.149123\n\n\n38\nEast\n52\n85.520635\n0.000000\n93.223684\n93.223684\n62.149123\n62.149123\n62.149123\n62.149123\n62.149123\n62.149123\n62.149123\n\n\n45\nNorth\n7\n46.166667\n58.940741\n58.940741\n58.940741\n0.000000\n117.881481\n58.940741\n58.940741\n58.940741\n58.940741\n58.940741\n\n\n59\nNorth\n10\n50.783333\n58.940741\n58.940741\n58.940741\n117.881481\n0.000000\n58.940741\n58.940741\n58.940741\n58.940741\n58.940741\n\n\n\n\n\n\n\n\n\n\n\n\n\nImportant\n\n\n\nFor any of the three methods, we can request the replicate coefficient instead of the replicate weights by using rep_coefs=True.\n\n\n\n#jackknife = ReplicateWeight(method=RepMethod.jackknife, strat=True)\njkn_wgt = jackknife.replicate(\n    samp_weight=full_sample[\"design_weight\"], \n    psu=full_sample[\"cluster\"], \n    stratum=full_sample[\"region\"], \n    rep_coefs=True\n)\n\njkn_wgt.drop_duplicates().sort_values(by=\"_stratum\").head(15)\n\n\n\n\n\n\n\n\n_stratum\n_psu\n_samp_weight\n_jk_wgt_1\n_jk_wgt_2\n_jk_wgt_3\n_jk_wgt_4\n_jk_wgt_5\n_jk_wgt_6\n_jk_wgt_7\n_jk_wgt_8\n_jk_wgt_9\n_jk_wgt_10\n\n\n\n\n0\nEast\n52\n85.520635\n0.0\n1.5\n1.5\n1.0\n1.0\n1.0\n1.0\n1.0\n1.0\n1.0\n\n\n1\nEast\n45\n85.398025\n1.5\n0.0\n1.5\n1.0\n1.0\n1.0\n1.0\n1.0\n1.0\n1.0\n\n\n20\nEast\n34\n75.661566\n1.5\n1.5\n0.0\n1.0\n1.0\n1.0\n1.0\n1.0\n1.0\n1.0\n\n\n45\nNorth\n7\n46.166667\n1.0\n1.0\n1.0\n0.0\n2.0\n1.0\n1.0\n1.0\n1.0\n1.0\n\n\n59\nNorth\n10\n50.783333\n1.0\n1.0\n1.0\n2.0\n0.0\n1.0\n1.0\n1.0\n1.0\n1.0\n\n\n75\nSouth\n29\n65.702778\n1.0\n1.0\n1.0\n1.0\n1.0\n0.0\n1.5\n1.5\n1.0\n1.0\n\n\n77\nSouth\n24\n58.940741\n1.0\n1.0\n1.0\n1.0\n1.0\n1.5\n0.0\n1.5\n1.0\n1.0\n\n\n82\nSouth\n16\n62.149123\n1.0\n1.0\n1.0\n1.0\n1.0\n1.5\n1.5\n0.0\n1.0\n1.0\n\n\n120\nWest\n86\n213.491667\n1.0\n1.0\n1.0\n1.0\n1.0\n1.0\n1.0\n1.0\n0.0\n2.0\n\n\n133\nWest\n64\n218.893889\n1.0\n1.0\n1.0\n1.0\n1.0\n1.0\n1.0\n1.0\n2.0\n0.0",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Weighting",
      "Replicate Weights"
    ]
  },
  {
    "objectID": "pages/categorical_tabulation.html",
    "href": "pages/categorical_tabulation.html",
    "title": "Tabulation",
    "section": "",
    "text": "In this tutorial, we will explore samplics’ APIs for creating design-based tabulations. There are two main python classes for tabulation i.e. Tabulation() for one-way tables and CrossTabulation() for two-way tables.\n\nfrom pprint import pprint\n\nfrom samplics.datasets import load_birth, load_nhanes2\nfrom samplics.categorical import Tabulation, CrossTabulation\n\nfrom samplics.utils.types import PopParam\n\n\nOne-way tabulation\nThe birth dataset has four variables: region, agecat, birthcat, and pop. The variables agecat and birthcat are categirical. By default, pandas read them as numerical, because they are coded with numerical values. We use dtype=\"string\" or dtype=\"category\" to ensure that pandas codes the variables as categorical responses.\n\n# Load Birth sample data\nbirth_dict = load_birth()\nbirth = birth_dict[\"data\"].astype(\n    {\"region\": str, \"agecat\": str, \"birthcat\": str}\n)\n\nregion = birth[\"region\"]\nagecat = birth[\"agecat\"]\nbirthcat = birth[\"birthcat\"]\n\nbirth.head(15)\n\n\n\n\n\n\n\n\nregion\nagecat\nbirthcat\npop\n\n\n\n\n0\n1\n1\n1.0\n28152\n\n\n1\n1\n1\n1.0\n103101\n\n\n2\n1\n1\n1.0\n113299\n\n\n3\n1\n1\n1.0\n112028\n\n\n4\n1\n1\n1.0\n99588\n\n\n5\n1\n1\n1.0\n22356\n\n\n6\n1\n1\n1.0\n102926\n\n\n7\n1\n1\n1.0\n12627\n\n\n8\n1\n1\n1.0\n112885\n\n\n9\n1\n1\n1.0\n150297\n\n\n10\n1\n1\n1.0\n52785\n\n\n11\n1\n1\n2.0\n109108\n\n\n12\n1\n1\n2.0\n87768\n\n\n13\n1\n1\n2.0\n175886\n\n\n14\n1\n1\n2.0\n107847\n\n\n\n\n\n\n\nWhen requesting a table, the user can set param=\"count\" which results in a tabulation with counts in the cells while param=\"proportion leads to cells with proportions. The expression Tabulation(\"count\") instantiates the class Tabulation() which has a method tabulate() to produce the table.\n\nbirth_count = Tabulation(param=PopParam.count)\nbirth_count.tabulate(birthcat, remove_nan=True)\n\nprint(birth_count)\n\n\nTabulation of birthcat\n Number of strata: 1\n Number of PSUs: 923\n Number of observations: 923\n Degrees of freedom: 922.00\n\n variable category  PopParam.count  stderror   lower_ci   upper_ci\nbirthcat      1.0           240.0 13.333695 213.832087 266.167913\nbirthcat      2.0           450.0 15.193974 420.181215 479.818785\nbirthcat      3.0           233.0 13.204959 207.084737 258.915263\n\n\n\nWhen remove_nan=False, the numpy and pandas special values NaNs, respectively np.nan and NaN, are treated as valid categories and added to the tables as shown below\n\nbirth_count = Tabulation(param=PopParam.count)\nbirth_count.tabulate(birthcat, remove_nan=False)\n\nprint(birth_count)\n\n\nTabulation of birthcat\n Number of strata: 1\n Number of PSUs: 956\n Number of observations: 956\n Degrees of freedom: 955.00\n\n variable category  PopParam.count  stderror   lower_ci   upper_ci\nbirthcat      1.0           240.0 13.414066 213.675550 266.324450\nbirthcat      2.0           450.0 15.441157 419.697485 480.302515\nbirthcat      3.0           233.0 13.281448 206.935807 259.064193\nbirthcat      nan            33.0  5.647499  21.917060  44.082940\n\n\n\nThe data associated with the tabulation are stored in nested python dictionaries. The higher level key is the variable name and the inner keys are the response categories. Each of the last four columns shown above are stored in separated dictionaries. Two of those dictionaries for the counts and standard errors shown below.\n\nprint(\"\\nThe designed-based estimated counts are:\")\npprint(birth_count.point_est)\n\nprint(\"\\nThe designed-based standard errors are:\")\npprint(birth_count.stderror)\n\n\nThe designed-based estimated counts are:\n{'birthcat': {'1.0': 240.0, '2.0': 450.0, '3.0': 233.0, 'nan': 33.0}}\n\nThe designed-based standard errors are:\n{'birthcat': {'1.0': 13.414066228212423,\n              '2.0': 15.441156672080215,\n              '3.0': 13.281447911983973,\n              'nan': 5.647498635475388}}\n\n\nSometimes, the user may want to run multiple one-way tables of several variables. In this case, the user can provide the data as a two-dimensional dataframe where each column represents one categorical variable. In this situation, each categorical variable is tabulated individually then combined into Python dictionaries.\n\nbirth_count2 = Tabulation(param=PopParam.count)\nbirth_count2.tabulate(\n    birth[[\"region\", \"agecat\", \"birthcat\"]], \n    remove_nan=True\n    )\n\nprint(birth_count2)\n\n\nTabulation of region\n Number of strata: 1\n Number of PSUs: 923\n Number of observations: 923\n Degrees of freedom: 922.00\n\n variable category  PopParam.count  stderror   lower_ci   upper_ci\n  region        1           166.0 11.718335 143.003340 188.996660\n  region        2           284.0 14.136507 256.257795 311.742205\n  region        3           250.0 13.594733 223.321002 276.678998\n  region        4           256.0 13.698320 229.117716 282.882284\n  agecat        1           507.0 15.439224 476.701278 537.298722\n  agecat        2           316.0 14.552307 287.441809 344.558191\n  agecat        3           133.0 10.705921 111.990152 154.009848\nbirthcat      1.0           240.0 13.333695 213.832087 266.167913\nbirthcat      2.0           450.0 15.193974 420.181215 479.818785\nbirthcat      3.0           233.0 13.204959 207.084737 258.915263\n\n\n\nTwo of the associated Python dictionaries are shown below. The structure of the inner dictionaries remain the same but additional key-value pairs are added to represent the several categorical variables.\n\nprint(\"\\nThe designed-based estimated counts are:\")\npprint(birth_count2.point_est)\n\nprint(\"\\nThe designed-based standard errors are:\")\npprint(birth_count2.stderror)\n\n\nThe designed-based estimated counts are:\n{'agecat': {'1': 507.0, '2': 316.0, '3': 133.0},\n 'birthcat': {'1.0': 240.0, '2.0': 450.0, '3.0': 233.0},\n 'region': {'1': 166.0, '2': 284.0, '3': 250.0, '4': 256.0}}\n\nThe designed-based standard errors are:\n{'agecat': {'1': 15.439223863518956,\n            '2': 14.552306810531896,\n            '3': 10.705921442206717},\n 'birthcat': {'1.0': 13.333694861331473,\n              '2.0': 15.193973574144477,\n              '3.0': 13.204958642679685},\n 'region': {'1': 11.718334853030456,\n            '2': 14.136507266518636,\n            '3': 13.594732580183479,\n            '4': 13.698320300591291}}\n\n\nIn the example above, we used pandas series and dataframes with labelled variables. In some situations, the user may want to tabulate numpy arrays, lists or tuples without variable names atrribute from the data. For these situations, the varnames parameter provides a way to assign names for the categorical variables. Even when the variables have labels, users can leverage varnames to rename the categorical variables.\n\nregion_no_name = birth[\"region\"].to_numpy()\nagecat_no_name = birth[\"agecat\"].to_numpy()\nbirthcat_no_name = birth[\"birthcat\"].to_numpy()\n\nbirth_prop_new_name = Tabulation(param=PopParam.prop)\nbirth_prop_new_name.tabulate(\n    vars=[region_no_name, agecat_no_name, birthcat_no_name],\n    varnames=[\"Region\", \"AgeGroup\", \"BirthType\"],\n    remove_nan=True,\n)\n\nprint(birth_prop_new_name)\n\n\nTabulation of Region\n Number of strata: 1\n Number of PSUs: 923\n Number of observations: 923\n Degrees of freedom: 922.00\n\n  variable category  PopParam.prop  stderror  lower_ci  upper_ci\n   Region        1       0.173640  0.012258  0.150883  0.199025\n   Region        2       0.297071  0.014787  0.268892  0.326883\n   Region        3       0.261506  0.014220  0.234574  0.290357\n   Region        4       0.267782  0.014329  0.240614  0.296819\n AgeGroup        1       0.530335  0.016150  0.498562  0.561864\n AgeGroup        2       0.330544  0.015222  0.301383  0.361068\n AgeGroup        3       0.139121  0.011199  0.118564  0.162586\nBirthType      1.0       0.260022  0.014446  0.232687  0.289357\nBirthType      2.0       0.487541  0.016462  0.455331  0.519854\nBirthType      3.0       0.252438  0.014307  0.225406  0.281533\n\n\n\nIf the user does not specify varnames, the tabulate() creates generic variables names var_1, var_2, etc.\n\nbirth_prop_new_name2 = Tabulation(param=PopParam.prop)\nbirth_prop_new_name2.tabulate(\n    vars=[region_no_name, agecat_no_name, birthcat_no_name], \n    remove_nan=True\n)\n\nprint(birth_prop_new_name2)\n\n\nTabulation of var_1\n Number of strata: 1\n Number of PSUs: 923\n Number of observations: 923\n Degrees of freedom: 922.00\n\n variable category  PopParam.prop  stderror  lower_ci  upper_ci\n   var_1        1       0.173640  0.012258  0.150883  0.199025\n   var_1        2       0.297071  0.014787  0.268892  0.326883\n   var_1        3       0.261506  0.014220  0.234574  0.290357\n   var_1        4       0.267782  0.014329  0.240614  0.296819\n   var_2        1       0.530335  0.016150  0.498562  0.561864\n   var_2        2       0.330544  0.015222  0.301383  0.361068\n   var_2        3       0.139121  0.011199  0.118564  0.162586\n   var_3      1.0       0.260022  0.014446  0.232687  0.289357\n   var_3      2.0       0.487541  0.016462  0.455331  0.519854\n   var_3      3.0       0.252438  0.014307  0.225406  0.281533\n\n\n\nIf the data was collected from a complex survey sample, the user may provide the sample design information to derive design-based statistics for the tabulation.\n\n# Load Nhanes sample data\nnhanes2_dict = load_nhanes2()\nnhanes2 = nhanes2_dict[\"data\"]\n\nstratum = nhanes2[\"stratid\"]\npsu = nhanes2[\"psuid\"]\nweight = nhanes2[\"finalwgt\"]\n\ndiabetes_nhanes = Tabulation(param=PopParam.prop)\ndiabetes_nhanes.tabulate(\n    vars=nhanes2[[\"race\", \"diabetes\"]],\n    samp_weight=weight,\n    stratum=stratum,\n    psu=psu,\n    remove_nan=True,\n)\n\nprint(diabetes_nhanes)\n\n\nTabulation of race\n Number of strata: 31\n Number of PSUs: 62\n Number of observations: 10335\n Degrees of freedom: 31.00\n\n variable  category  PopParam.prop  stderror  lower_ci  upper_ci\n    race       1.0       0.879016  0.016722  0.840568  0.909194\n    race       2.0       0.095615  0.012778  0.072541  0.125039\n    race       3.0       0.025369  0.010554  0.010781  0.058528\ndiabetes       0.0       0.965715  0.001820  0.961803  0.969238\ndiabetes       1.0       0.034285  0.001820  0.030762  0.038197\n\n\n\n\n\nTwo-way tabulation (cross-tabulation)\nCross-tabulation of two categorical variables is achieved by using the class CrossTabulation(). As above, cross-tabulation is possible for counts and proportions using CrossTabulation(param=\"count\") and CrossTabulation(param=\"proportion\"), respectively. The Python script below creates a design-based cross-tabulation of race by diabetes status. The sample design information is optional; when not provided, a simple random sample (srs) is assumed.\n\ncrosstab_nhanes = CrossTabulation(param=PopParam.prop)\ncrosstab_nhanes.tabulate(\n    vars=nhanes2[[\"race\", \"diabetes\"]],\n    samp_weight=weight,\n    stratum=stratum,\n    psu=psu,\n    remove_nan=True,\n)\n\nprint(crosstab_nhanes)\n\n\nCross-tabulation of race and diabetes\n Number of strata: 31\n Number of PSUs: 62\n Number of observations: 10335\n Degrees of freedom: 31.00\n\n race diabetes  PopParam.prop  stderror  lower_ci  upper_ci\n   1      0.0       0.850866  0.015850  0.815577  0.880392\n   1      1.0       0.028123  0.001938  0.024430  0.032357\n   2      0.0       0.089991  0.012171  0.068062  0.118090\n   2      1.0       0.005646  0.000847  0.004157  0.007663\n   3      0.0       0.024858  0.010188  0.010702  0.056669\n   3      1.0       0.000516  0.000387  0.000112  0.002383\n\nPearson (with Rao-Scott adjustment):\n    Unadjusted - chi2(2): 21.2661 with p-value of 0.0000\n    Adjusted - F(1.52, 47.26): 14.9435  with p-value of 0.0000\n\n  Likelihood ratio (with Rao-Scott adjustment):\n     Unadjusted - chi2(2): 18.3925 with p-value of 0.0001\n     Adjusted - F(1.52, 47.26): 12.9242  with p-value of 0.0001\n\n\n\nIn addition to pandas dataframe, the categorical variables may be provided as an numpy array, list or tuple. In the examples below, the categorical variables are provided as a tuple vars=(rage, diabetes). In this case, race and diabetes are numpy arrays and do not have a name attribute. The parameter varnames allows the user to name the categorical variables. If varnames is not specified then `var_1 and var_2 are used as variables names.\n\nrace = nhanes2[\"race\"].to_numpy()\ndiabetes = nhanes2[\"diabetes\"].to_numpy()\n\ncrosstab_nhanes = CrossTabulation(param=PopParam.prop)\ncrosstab_nhanes.tabulate(\n    vars=(race, diabetes),\n    samp_weight=weight,\n    stratum=stratum,\n    psu=psu,\n    remove_nan=True,\n)\n\nprint(crosstab_nhanes)\n\n\nCross-tabulation of __var1__ and __var2__\n Number of strata: 31\n Number of PSUs: 62\n Number of observations: 10335\n Degrees of freedom: 31.00\n\n __var1__ __var2__  PopParam.prop  stderror  lower_ci  upper_ci\n     1.0      0.0       0.850866  0.015850  0.815577  0.880392\n     1.0      1.0       0.028123  0.001938  0.024430  0.032357\n     2.0      0.0       0.089991  0.012171  0.068062  0.118090\n     2.0      1.0       0.005646  0.000847  0.004157  0.007663\n     3.0      0.0       0.024858  0.010188  0.010702  0.056669\n     3.0      1.0       0.000516  0.000387  0.000112  0.002383\n\nPearson (with Rao-Scott adjustment):\n    Unadjusted - chi2(2): 21.2661 with p-value of 0.0000\n    Adjusted - F(1.52, 47.26): 14.9435  with p-value of 0.0000\n\n  Likelihood ratio (with Rao-Scott adjustment):\n     Unadjusted - chi2(2): 18.3925 with p-value of 0.0001\n     Adjusted - F(1.52, 47.26): 12.9242  with p-value of 0.0001\n\n\n\nSame as the above example with variables names specified by varnames=[\"Race\", DiabetesStatus\"]\n\ncrosstab_nhanes = CrossTabulation(param=PopParam.prop)\ncrosstab_nhanes.tabulate(\n    vars=(race, diabetes),\n    varnames=[\"Race\", \"DiabetesStatus\"],\n    samp_weight=weight,\n    stratum=stratum,\n    psu=psu,\n    remove_nan=True,\n)\n\nprint(crosstab_nhanes)\n\n\nCross-tabulation of Race and DiabetesStatus\n Number of strata: 31\n Number of PSUs: 62\n Number of observations: 10335\n Degrees of freedom: 31.00\n\n Race DiabetesStatus  PopParam.prop  stderror  lower_ci  upper_ci\n 1.0            0.0       0.850866  0.015850  0.815577  0.880392\n 1.0            1.0       0.028123  0.001938  0.024430  0.032357\n 2.0            0.0       0.089991  0.012171  0.068062  0.118090\n 2.0            1.0       0.005646  0.000847  0.004157  0.007663\n 3.0            0.0       0.024858  0.010188  0.010702  0.056669\n 3.0            1.0       0.000516  0.000387  0.000112  0.002383\n\nPearson (with Rao-Scott adjustment):\n    Unadjusted - chi2(2): 21.2661 with p-value of 0.0000\n    Adjusted - F(1.52, 47.26): 14.9435  with p-value of 0.0000\n\n  Likelihood ratio (with Rao-Scott adjustment):\n     Unadjusted - chi2(2): 18.3925 with p-value of 0.0001\n     Adjusted - F(1.52, 47.26): 12.9242  with p-value of 0.0001",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Categorical Data Analysis",
      "Tabulation"
    ]
  },
  {
    "objectID": "pages/size_stagedesign.html",
    "href": "pages/size_stagedesign.html",
    "title": "Sample Size for Stage Design",
    "section": "",
    "text": "In the cells below, we illustrate a simple example of sample size calculation in the context of household surveys using stage sampling designs. Let’s assume that we want to calculate sample size for a vaccination survey in Senegal. We want to stratify the sample by administrative region. We will use the 2017 Senegal Demographic and Health Survey (DHS) to get an idea of the vaccination coverage rates for some main vaccine-doses. Below, we show coverage rates of hepatitis B birth dose (hepB0) vaccine, first and third dose of diphtheria, tetanus and pertussis (DTP), first dose of measles containing vaccine (MCV1) and coverage of basic vaccination. Basic vaccination refers to the 12-23 months old children that received BCG vaccine, three doses of DTP containing vaccine, three doses of polio vaccine, and the first dose of measles containing vaccine.The table below shows the 2017 Senegal DHS vaccination coverage of a few vaccine-doses for children aged 12 to 23 months old.\nThe 2017 Senegal DHS data collection happened from April to December 2018. Therefore, the data shown in the table represent children born from October 2016 to December 2017. For the purpose of this tutorial, we will assume that these vaccine coverage rates still hold. Furthermore, we will use the basic vaccination coverage rates to calculate sample size.\nfrom samplics.sampling import SampleSize",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Size Calculation",
      "Sample Size for Stage Design"
    ]
  },
  {
    "objectID": "pages/size_stagedesign.html#wald-method",
    "href": "pages/size_stagedesign.html#wald-method",
    "title": "Sample Size for Stage Design",
    "section": "Wald method",
    "text": "Wald method\nThe first step is to create and object using the SampleSize class with the parameter of interest, the sample size calculation method, and the stratification status. In this example, we want to calculate sample size for proportions, using wald method for a stratified design. This is achived with the following snippet of code.\n\nSampleSize(\n    param=\"proportion\", method=\"wald\", strat=True\n)\n\nBecause, we are using a stratified sample design, it is best to specify the expected coverage levels by stratum. If the information is not available then aggregated values can be used across the strata. The 2017 Senegal DHS published the coverage rates by region hence we have the information available by stratum. To provide the informmation to Samplics we use the python dictionaries as follows\n\nexpected_coverage = {\n    \"Dakar\": 0.849,\n    \"Ziguinchor\": 0.809,\n    \"Diourbel\": 0.682,\n    \"Saint-Louis\": 0.806,\n    \"Tambacounda\": 0.470,\n    \"Kaolack\": 0.797,\n    \"Thies\": 0.834,\n    \"Louga\": 0.678,\n    \"Fatick\": 0.766,\n    \"Kolda\": 0.637,\n    \"Matam\": 0.687,\n    \"Kaffrine\": 0.766,\n    \"Kedougou\": 0.336,\n    \"Sedhiou\": 0.742,\n}\n\nNow, we want to calculate the sample size with desired precision of 0.07 which means that we want the expected vaccination coverage rates to have 7% half confidence intervals e.g. expected rate of 90% will have a confidence interval of [83%, 97%]. Note that the desired precision can be specified by stratum in a similar way as the target coverage using a python dictionary.\nGiven that information, we can calculate the sample size using SampleSize class as follows.\n\nfrom samplics.utils.types import SizeMethod, PopParam\n\n# Declare the sample size calculation parameters\nsen_vaccine_wald = SampleSize(\n    param=PopParam.prop, method=SizeMethod.wald, strat=True\n)\n\n# calculate the sample size\nsen_vaccine_wald.calculate(target=expected_coverage, half_ci=0.07)\n\n# show the calculated sample size\nprint(f\"\\nCalculated sample sizes by stratum: \")\nsen_vaccine_wald.samp_size\n\n\nCalculated sample sizes by stratum: \n\n\n{'Dakar': 101,\n 'Ziguinchor': 122,\n 'Diourbel': 171,\n 'Saint-Louis': 123,\n 'Tambacounda': 196,\n 'Kaolack': 127,\n 'Thies': 109,\n 'Louga': 172,\n 'Fatick': 141,\n 'Kolda': 182,\n 'Matam': 169,\n 'Kaffrine': 141,\n 'Kedougou': 175,\n 'Sedhiou': 151}\n\n\nSampleSize calculates the sample sizes and store the in teh samp_size attributes which is a python dictinary object. If a dataframe is better suited for the use case, the method to_dataframe() can be used to return a pandas dataframe.\n\nsen_vaccine_wald.to_dataframe()\n\n\n\n\n\n\n\n\n_param\n_stratum\n_target\n_sigma\n_half_ci\n_samp_size\n\n\n\n\n0\nPopParam.prop\nDakar\n0.849\n0.128199\n0.07\n101\n\n\n1\nPopParam.prop\nZiguinchor\n0.809\n0.154519\n0.07\n122\n\n\n2\nPopParam.prop\nDiourbel\n0.682\n0.216876\n0.07\n171\n\n\n3\nPopParam.prop\nSaint-Louis\n0.806\n0.156364\n0.07\n123\n\n\n4\nPopParam.prop\nTambacounda\n0.470\n0.249100\n0.07\n196\n\n\n5\nPopParam.prop\nKaolack\n0.797\n0.161791\n0.07\n127\n\n\n6\nPopParam.prop\nThies\n0.834\n0.138444\n0.07\n109\n\n\n7\nPopParam.prop\nLouga\n0.678\n0.218316\n0.07\n172\n\n\n8\nPopParam.prop\nFatick\n0.766\n0.179244\n0.07\n141\n\n\n9\nPopParam.prop\nKolda\n0.637\n0.231231\n0.07\n182\n\n\n10\nPopParam.prop\nMatam\n0.687\n0.215031\n0.07\n169\n\n\n11\nPopParam.prop\nKaffrine\n0.766\n0.179244\n0.07\n141\n\n\n12\nPopParam.prop\nKedougou\n0.336\n0.223104\n0.07\n175\n\n\n13\nPopParam.prop\nSedhiou\n0.742\n0.191436\n0.07\n151\n\n\n\n\n\n\n\nThe sample size calculation above assumes that the design effect (DEFF) was equal to 1. A design effect of 1 correspond to sampling design with a variance equivalent to a simple random selection of same sample size. In the context of complex sampling designs, DEFF is often different from 1. Stage sampling and unequal weights usually increase the design effect above 1. The 2017 Senegal DHS indicated a design effect equal to 1.963 (1.401^2) for basic vaccination. Hence, to calculate the sample size, we will use the design effect provided by DHS.\n\nsen_vaccine_wald.calculate(\n    target=expected_coverage, half_ci=0.07, deff=1.401 ** 2\n)\n\nsen_vaccine_wald.to_dataframe()\n\n\n\n\n\n\n\n\n_param\n_stratum\n_target\n_sigma\n_half_ci\n_samp_size\n\n\n\n\n0\nPopParam.prop\nDakar\n0.849\n0.128199\n0.07\n198\n\n\n1\nPopParam.prop\nZiguinchor\n0.809\n0.154519\n0.07\n238\n\n\n2\nPopParam.prop\nDiourbel\n0.682\n0.216876\n0.07\n334\n\n\n3\nPopParam.prop\nSaint-Louis\n0.806\n0.156364\n0.07\n241\n\n\n4\nPopParam.prop\nTambacounda\n0.470\n0.249100\n0.07\n384\n\n\n5\nPopParam.prop\nKaolack\n0.797\n0.161791\n0.07\n249\n\n\n6\nPopParam.prop\nThies\n0.834\n0.138444\n0.07\n214\n\n\n7\nPopParam.prop\nLouga\n0.678\n0.218316\n0.07\n336\n\n\n8\nPopParam.prop\nFatick\n0.766\n0.179244\n0.07\n276\n\n\n9\nPopParam.prop\nKolda\n0.637\n0.231231\n0.07\n356\n\n\n10\nPopParam.prop\nMatam\n0.687\n0.215031\n0.07\n331\n\n\n11\nPopParam.prop\nKaffrine\n0.766\n0.179244\n0.07\n276\n\n\n12\nPopParam.prop\nKedougou\n0.336\n0.223104\n0.07\n344\n\n\n13\nPopParam.prop\nSedhiou\n0.742\n0.191436\n0.07\n295\n\n\n\n\n\n\n\nSince the sample design is stratified, the sample size calculation will be more precised if DEFF is specified at the stratum level which is available from the 2017 Senegal DHS provided report. Some regions have a design effect below 1. To be conservative with our sample size calculation, we will use 1.21 as the minimum design effect to use in the sample size calculation.\n\n# Target coverage rates\nexpected_deff = {\n    \"Dakar\": 1.100 ** 2,\n    \"Ziguinchor\": 1.100 ** 2,\n    \"Diourbel\": 1.346 ** 2,\n    \"Saint-Louis\": 1.484 ** 2,\n    \"Tambacounda\": 1.366 ** 2,\n    \"Kaolack\": 1.360 ** 2,\n    \"Thies\": 1.109 ** 2,\n    \"Louga\": 1.902 ** 2,\n    \"Fatick\": 1.100 ** 2,\n    \"Kolda\": 1.217 ** 2,\n    \"Matam\": 1.403 ** 2,\n    \"Kaffrine\": 1.256 ** 2,\n    \"Kedougou\": 2.280 ** 2,\n    \"Sedhiou\": 1.335 ** 2,\n}\n\n# Calculate sample sizes using deff at the stratum level\nsen_vaccine_wald.calculate(\n    target=expected_coverage, half_ci=0.07, deff=expected_deff\n)\n\n# Convert sample sizes to a dataframe\nsen_vaccine_wald.to_dataframe()\n\n\n\n\n\n\n\n\n_param\n_stratum\n_target\n_sigma\n_half_ci\n_samp_size\n\n\n\n\n0\nPopParam.prop\nDakar\n0.849\n0.128199\n0.07\n122\n\n\n1\nPopParam.prop\nZiguinchor\n0.809\n0.154519\n0.07\n147\n\n\n2\nPopParam.prop\nDiourbel\n0.682\n0.216876\n0.07\n309\n\n\n3\nPopParam.prop\nSaint-Louis\n0.806\n0.156364\n0.07\n270\n\n\n4\nPopParam.prop\nTambacounda\n0.470\n0.249100\n0.07\n365\n\n\n5\nPopParam.prop\nKaolack\n0.797\n0.161791\n0.07\n235\n\n\n6\nPopParam.prop\nThies\n0.834\n0.138444\n0.07\n134\n\n\n7\nPopParam.prop\nLouga\n0.678\n0.218316\n0.07\n620\n\n\n8\nPopParam.prop\nFatick\n0.766\n0.179244\n0.07\n171\n\n\n9\nPopParam.prop\nKolda\n0.637\n0.231231\n0.07\n269\n\n\n10\nPopParam.prop\nMatam\n0.687\n0.215031\n0.07\n332\n\n\n11\nPopParam.prop\nKaffrine\n0.766\n0.179244\n0.07\n222\n\n\n12\nPopParam.prop\nKedougou\n0.336\n0.223104\n0.07\n910\n\n\n13\nPopParam.prop\nSedhiou\n0.742\n0.191436\n0.07\n268\n\n\n\n\n\n\n\nThe sample size calculation above does not account for attrition of sample sizes due to non-response. In the 2017 Semegal DHS, the overal household and women reponse rate was abou 94.2%.\n\n# Calculate sample sizes with a resp_rate of 94.2%\nsen_vaccine_wald.calculate(\n    target=expected_coverage, \n    half_ci=0.07, \n    deff=expected_deff, \n    resp_rate=0.942\n)\n\n# Convert sample sizes to a dataframe\nsen_vaccine_wald.to_dataframe(\n    col_names=[\n        \"Parameter\",\n        \"region\",\n        \"vaccine_cov\",\n        \"stderr\",\n        \"half_ci\",\n        \"count_12_23\",\n    ]\n)\n\n\n\n\n\n\n\n\nParameter\nregion\nvaccine_cov\nstderr\nhalf_ci\ncount_12_23\n\n\n\n\n0\nPopParam.prop\nDakar\n0.849\n0.128199\n0.07\n130\n\n\n1\nPopParam.prop\nZiguinchor\n0.809\n0.154519\n0.07\n156\n\n\n2\nPopParam.prop\nDiourbel\n0.682\n0.216876\n0.07\n328\n\n\n3\nPopParam.prop\nSaint-Louis\n0.806\n0.156364\n0.07\n287\n\n\n4\nPopParam.prop\nTambacounda\n0.470\n0.249100\n0.07\n387\n\n\n5\nPopParam.prop\nKaolack\n0.797\n0.161791\n0.07\n250\n\n\n6\nPopParam.prop\nThies\n0.834\n0.138444\n0.07\n142\n\n\n7\nPopParam.prop\nLouga\n0.678\n0.218316\n0.07\n658\n\n\n8\nPopParam.prop\nFatick\n0.766\n0.179244\n0.07\n181\n\n\n9\nPopParam.prop\nKolda\n0.637\n0.231231\n0.07\n286\n\n\n10\nPopParam.prop\nMatam\n0.687\n0.215031\n0.07\n353\n\n\n11\nPopParam.prop\nKaffrine\n0.766\n0.179244\n0.07\n236\n\n\n12\nPopParam.prop\nKedougou\n0.336\n0.223104\n0.07\n966\n\n\n13\nPopParam.prop\nSedhiou\n0.742\n0.191436\n0.07\n284",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Size Calculation",
      "Sample Size for Stage Design"
    ]
  },
  {
    "objectID": "pages/size_stagedesign.html#fleiss-method",
    "href": "pages/size_stagedesign.html#fleiss-method",
    "title": "Sample Size for Stage Design",
    "section": "Fleiss method",
    "text": "Fleiss method\nThe World Health Organization (WHO) recommends using the Fleiss method for calculating sample size for vaccination coverage survey, as specified in the following guideline document: https://www.who.int/immunization/documents/who_ivb_18.09/en/. To use the Fleiss method, the examples shown above are the same with method=\"fleiss\".\n\nsen_vaccine_fleiss = SampleSize(\n    param=PopParam.prop, \n    method=SizeMethod.fleiss, \n    strat=True\n)\n\nsen_vaccine_fleiss.calculate(\n    target=expected_coverage, \n    half_ci=0.07, \n    deff=expected_deff, \n    resp_rate=0.942\n)\n\nsen_vaccine_sample = sen_vaccine_fleiss.to_dataframe(\n    col_names=[\n        \"Parameter\",\n        \"region\",\n        \"vaccine_cov\",\n        \"stderr\",\n        \"half_ci\",\n        \"count_12_23\",\n    ]\n)\nsen_vaccine_sample.head(15)\n\n\n\n\n\n\n\n\nParameter\nregion\nvaccine_cov\nstderr\nhalf_ci\ncount_12_23\n\n\n\n\n0\nPopParam.prop\nDakar\n0.849\n0.128199\n0.07\n190\n\n\n1\nPopParam.prop\nZiguinchor\n0.809\n0.154519\n0.07\n210\n\n\n2\nPopParam.prop\nDiourbel\n0.682\n0.216876\n0.07\n398\n\n\n3\nPopParam.prop\nSaint-Louis\n0.806\n0.156364\n0.07\n384\n\n\n4\nPopParam.prop\nTambacounda\n0.470\n0.249100\n0.07\n410\n\n\n5\nPopParam.prop\nKaolack\n0.797\n0.161791\n0.07\n329\n\n\n6\nPopParam.prop\nThies\n0.834\n0.138444\n0.07\n201\n\n\n7\nPopParam.prop\nLouga\n0.678\n0.218316\n0.07\n794\n\n\n8\nPopParam.prop\nFatick\n0.766\n0.179244\n0.07\n228\n\n\n9\nPopParam.prop\nKolda\n0.637\n0.231231\n0.07\n325\n\n\n10\nPopParam.prop\nMatam\n0.687\n0.215031\n0.07\n432\n\n\n11\nPopParam.prop\nKaffrine\n0.766\n0.179244\n0.07\n297\n\n\n12\nPopParam.prop\nKedougou\n0.336\n0.223104\n0.07\n1140\n\n\n13\nPopParam.prop\nSedhiou\n0.742\n0.191436\n0.07\n348\n\n\n\n\n\n\n\nAt this point, we have the number of 12-23 months needed to achieve the desired precision given the expected proportions using wald or fleiss calculation methods.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Size Calculation",
      "Sample Size for Stage Design"
    ]
  },
  {
    "objectID": "pages/categorical.html",
    "href": "pages/categorical.html",
    "title": "Overview",
    "section": "",
    "text": "Categorical data analysis refers to the set of methods for analyzing categorical response variables. This is a large topic in data analysis and used in the context of complex survey sampling. Contingency tables or cross-tabulations with associated statistics are well-known categorical data analysis techniques. But categorical data analysis extends to more areas such as modeling techniques for categorical response variables e.g. logistic/logit/loglinear regressions and generalized linear mixed models for categorical responses.\nSection 1: Tabulation  Section 2: T-Test \nAgresti (2013) provides an overview of these methods.\n\n\n\n\nReferences\n\nAgresti, Alan. 2013. Categorical Data Analysis, 3rd edn. John Wiley & Sons, Hoboken, New Jersey.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Categorical Data Analysis",
      "Overview"
    ]
  },
  {
    "objectID": "pages/weight.html",
    "href": "pages/weight.html",
    "title": "Overview",
    "section": "",
    "text": "The sample weighting is the mechanism that allows us to generalize the results from the sample to the target population. The design/base weights are obtained from the final probability of selection as their inverse. In large scale surveys, design weights are adjusted to account for non-response, for extreme values, or to ensure that auxiliary variables benchmark to known controls.\nThe weighting tutorial is in two parts. The first part discusses several weight adjustments methods e.g. non-response adjustment, poststratification, calibration. The second part of the tutorial walks the user through the creation of replicate weights using Booststrap, Balanced Repeated Replication (BRR), and Jackknife methods.\nSection 1: Sample weight adjustments  Section 2: Replicates weights \nValliant, R. and Dever, J. A. (2018) (Valliant and Dever 2018) provides a step-by-step guide on calculating sample weights.\n\n\n\n\nReferences\n\nValliant, R, and J A Dever. 2018. Survey Weights: A Step-by-Step Guide to Calculation. Stata Press.",
    "crumbs": [
      "Overview",
      "Tutorial",
      "Weighting",
      "Overview"
    ]
  }
]