[{"data":1,"prerenderedAt":1883},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fspatial-statistics-and-pattern-analysis\u002Fgenerate-random-points-and-sampling-pyqgis":3},{"id":4,"title":5,"body":6,"description":1872,"extension":1873,"meta":1874,"navigation":186,"path":1879,"seo":1880,"stem":1881,"__hash__":1882},"docs\u002Fspatial-data-processing-automation\u002Fspatial-statistics-and-pattern-analysis\u002Fgenerate-random-points-and-sampling-pyqgis\u002Findex.md","Generate Random Points and Spatial Samples in PyQGIS",{"type":7,"value":8,"toc":1858},"minimark",[9,13,17,26,115,120,133,137,145,390,421,425,428,604,613,617,620,711,829,838,842,845,939,951,955,958,1026,1287,1296,1300,1303,1708,1718,1722,1748,1752,1782,1786,1792,1796,1802,1808,1814,1820,1824,1854],[10,11,5],"h1",{"id":12},"generate-random-points-and-spatial-samples-in-pyqgis",[14,15,16],"p",{},"Randomness has two jobs in spatial work. The first is sampling: choosing field survey sites, plots for a vegetation inventory, households for an interview, or a subset of features for manual quality checks — in a way nobody can accuse of bias. The second is comparison: generating the random pattern that an observed pattern is tested against, so that \"these points are clustered\" means \"more clustered than chance would produce here\". QGIS has Processing algorithms for both, and a few lines of Python make them reproducible.",[14,18,19,20,25],{},"This recipe belongs to ",[21,22,24],"a",{"href":23},"\u002Fspatial-data-processing-automation\u002Fspatial-statistics-and-pattern-analysis\u002F","Spatial Statistics & Pattern Analysis",". It generates random points in polygons, on lines and in an extent, samples existing features at random and by stratum, enforces minimum distances, fixes seeds for reproducibility and uses random points as a baseline for a simple Monte Carlo test.",[14,27,28],{},[29,30,35,39,43,50,59,69,75,81,85,89,94,99,103,106,109,112],"svg",{"viewBox":31,"role":32,"ariaLabel":33,"xmlns":34},"0 0 760 280","img","Two families of random sampling: new random locations inside polygons, on lines or in an extent, and random subsets of existing features, overall or per stratum","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[36,37,38],"title",{},"Random locations and random features",[40,41,42],"desc",{},"Two families of random sampling. Random locations: new points generated inside polygons, along lines or within an extent, optionally with a minimum distance between them. Random features: a subset of existing features chosen at random from the whole layer or within each stratum such as a district. Both can be seeded so the same sample is drawn again.",[44,45],"rect",{"x":46,"y":46,"width":47,"height":48,"fill":49},"0","760","280","#f6f3ea",[51,52,58],"text",{"x":53,"y":54,"style":55,"fill":56,"textAnchor":57},"380","28","text-anchor:middle;font-size:14px;font-family:sans-serif;font-weight:bold","#17211d","middle","New locations, or a subset of what exists",[44,60],{"x":61,"y":62,"width":63,"height":64,"rx":65,"fill":66,"stroke":67,"style":68},"24","52","340","196","8","#eef7f4","#0f766e","stroke-width:2",[51,70,74],{"x":71,"y":72,"style":73,"fill":67,"textAnchor":57},"194","93.78","text-anchor:middle;font-size:11.5px;font-family:sans-serif;font-weight:bold","random locations",[51,76,80],{"x":71,"y":77,"style":78,"fill":79,"textAnchor":57},"123.78","text-anchor:middle;font-size:10.5px;font-family:sans-serif","#2f3b35","inside polygons",[51,82,84],{"x":71,"y":83,"style":78,"fill":79,"textAnchor":57},"153.78","along lines",[51,86,88],{"x":71,"y":87,"style":78,"fill":79,"textAnchor":57},"183.78","within an extent",[51,90,93],{"x":71,"y":91,"style":78,"fill":92,"textAnchor":57},"213.78","#59645f","min distance · seed",[44,95],{"x":96,"y":62,"width":63,"height":64,"rx":65,"fill":97,"stroke":98,"style":68},"396","#eff3ff","#2563eb",[51,100,102],{"x":101,"y":72,"style":73,"fill":98,"textAnchor":57},"566","random features",[51,104,105],{"x":101,"y":77,"style":78,"fill":79,"textAnchor":57},"n or % of the layer",[51,107,108],{"x":101,"y":83,"style":78,"fill":79,"textAnchor":57},"per stratum (district)",[51,110,111],{"x":101,"y":87,"style":78,"fill":92,"textAnchor":57},"reviews · surveys",[51,113,114],{"x":101,"y":91,"style":78,"fill":92,"textAnchor":57},"seed for repeatability",[116,117,119],"h2",{"id":118},"prerequisites","Prerequisites",[121,122,123,127,130],"ul",{},[124,125,126],"li",{},"QGIS 3.34 LTR or newer, or the QGIS 4 series.",[124,128,129],{},"Layers in a projected CRS so that minimum distances and densities are in metres.",[124,131,132],{},"A sampling design decided before you start: how many points, where, with what spacing, stratified by what.",[116,134,136],{"id":135},"random-points-inside-polygons","Random points inside polygons",[14,138,139,140,144],{},"The most common request is a fixed number of points inside each polygon — survey plots per forest stand, sample sites per field. ",[141,142,143],"code",{},"native:randompointsinpolygons"," does this with a count per polygon, a minimum distance and a seed.",[146,147,152],"pre",{"className":148,"code":149,"language":150,"meta":151,"style":151},"language-python shiki shiki-themes github-dark","import processing\nfrom qgis.core import QgsProject\n\nstands = QgsProject.instance().mapLayersByName(\"forest_stands\")[0]\nplots = processing.run(\"native:randompointsinpolygons\", {\n    \"INPUT\": stands,\n    \"POINTS_NUMBER\": 5,\n    \"MIN_DISTANCE\": 50,         # metres between plots\n    \"MIN_DISTANCE_GLOBAL\": 0,\n    \"MAX_TRIES_PER_POINT\": 50,\n    \"SEED\": 20261002,\n    \"INCLUDE_POLYGON_ATTRIBUTES\": True,\n    \"OUTPUT\": \"memory:survey_plots\",\n})\nlayer = plots[\"OUTPUT\"]\nprint(layer.featureCount(), \"plots;\", plots[\"POINTS_MISSED\"], \"could not be placed\")\nQgsProject.instance().addMapLayer(layer)\n","python","",[141,153,154,167,181,188,213,230,239,254,272,284,296,309,322,335,341,357,384],{"__ignoreMap":151},[155,156,159,163],"span",{"class":157,"line":158},"line",1,[155,160,162],{"class":161},"snl16","import",[155,164,166],{"class":165},"s95oV"," processing\n",[155,168,170,173,176,178],{"class":157,"line":169},2,[155,171,172],{"class":161},"from",[155,174,175],{"class":165}," qgis.core ",[155,177,162],{"class":161},[155,179,180],{"class":165}," QgsProject\n",[155,182,184],{"class":157,"line":183},3,[155,185,187],{"emptyLinePlaceholder":186},true,"\n",[155,189,191,194,197,200,204,207,210],{"class":157,"line":190},4,[155,192,193],{"class":165},"stands ",[155,195,196],{"class":161},"=",[155,198,199],{"class":165}," QgsProject.instance().mapLayersByName(",[155,201,203],{"class":202},"sU2Wk","\"forest_stands\"",[155,205,206],{"class":165},")[",[155,208,46],{"class":209},"sDLfK",[155,211,212],{"class":165},"]\n",[155,214,216,219,221,224,227],{"class":157,"line":215},5,[155,217,218],{"class":165},"plots ",[155,220,196],{"class":161},[155,222,223],{"class":165}," processing.run(",[155,225,226],{"class":202},"\"native:randompointsinpolygons\"",[155,228,229],{"class":165},", {\n",[155,231,233,236],{"class":157,"line":232},6,[155,234,235],{"class":202},"    \"INPUT\"",[155,237,238],{"class":165},": stands,\n",[155,240,242,245,248,251],{"class":157,"line":241},7,[155,243,244],{"class":202},"    \"POINTS_NUMBER\"",[155,246,247],{"class":165},": ",[155,249,250],{"class":209},"5",[155,252,253],{"class":165},",\n",[155,255,257,260,262,265,268],{"class":157,"line":256},8,[155,258,259],{"class":202},"    \"MIN_DISTANCE\"",[155,261,247],{"class":165},[155,263,264],{"class":209},"50",[155,266,267],{"class":165},",         ",[155,269,271],{"class":270},"sjoCn","# metres between plots\n",[155,273,275,278,280,282],{"class":157,"line":274},9,[155,276,277],{"class":202},"    \"MIN_DISTANCE_GLOBAL\"",[155,279,247],{"class":165},[155,281,46],{"class":209},[155,283,253],{"class":165},[155,285,287,290,292,294],{"class":157,"line":286},10,[155,288,289],{"class":202},"    \"MAX_TRIES_PER_POINT\"",[155,291,247],{"class":165},[155,293,264],{"class":209},[155,295,253],{"class":165},[155,297,299,302,304,307],{"class":157,"line":298},11,[155,300,301],{"class":202},"    \"SEED\"",[155,303,247],{"class":165},[155,305,306],{"class":209},"20261002",[155,308,253],{"class":165},[155,310,312,315,317,320],{"class":157,"line":311},12,[155,313,314],{"class":202},"    \"INCLUDE_POLYGON_ATTRIBUTES\"",[155,316,247],{"class":165},[155,318,319],{"class":209},"True",[155,321,253],{"class":165},[155,323,325,328,330,333],{"class":157,"line":324},13,[155,326,327],{"class":202},"    \"OUTPUT\"",[155,329,247],{"class":165},[155,331,332],{"class":202},"\"memory:survey_plots\"",[155,334,253],{"class":165},[155,336,338],{"class":157,"line":337},14,[155,339,340],{"class":165},"})\n",[155,342,344,347,349,352,355],{"class":157,"line":343},15,[155,345,346],{"class":165},"layer ",[155,348,196],{"class":161},[155,350,351],{"class":165}," plots[",[155,353,354],{"class":202},"\"OUTPUT\"",[155,356,212],{"class":165},[155,358,360,363,366,369,372,375,378,381],{"class":157,"line":359},16,[155,361,362],{"class":209},"print",[155,364,365],{"class":165},"(layer.featureCount(), ",[155,367,368],{"class":202},"\"plots;\"",[155,370,371],{"class":165},", plots[",[155,373,374],{"class":202},"\"POINTS_MISSED\"",[155,376,377],{"class":165},"], ",[155,379,380],{"class":202},"\"could not be placed\"",[155,382,383],{"class":165},")\n",[155,385,387],{"class":157,"line":386},17,[155,388,389],{"class":165},"QgsProject.instance().addMapLayer(layer)\n",[14,391,392,396,397,400,401,404,405,408,409,412,413,416,417,420],{},[393,394,395],"strong",{},"Breakdown:"," ",[141,398,399],{},"POINTS_NUMBER"," accepts a fixed count or, through a data-defined override, an expression — for example ",[141,402,403],{},"ceil($area \u002F 100000)"," for one plot per ten hectares. ",[141,406,407],{},"MIN_DISTANCE"," applies within each polygon; ",[141,410,411],{},"MIN_DISTANCE_GLOBAL"," across all of them. The algorithm tries up to ",[141,414,415],{},"MAX_TRIES_PER_POINT"," random locations before giving up on a point, and ",[141,418,419],{},"POINTS_MISSED"," reports how many could not be placed — a non-zero value means the spacing is too tight for small polygons. The seed makes the result repeatable: the same seed, inputs and parameters give the same points.",[116,422,424],{"id":423},"random-points-along-lines-and-in-an-extent","Random points along lines and in an extent",[14,426,427],{},"Transect surveys sample along roads, rivers or hedgerows; pattern tests often need random points in a rectangle. Both have their own algorithms with the same seed and spacing options.",[146,429,431],{"className":148,"code":430,"language":150,"meta":151,"style":151},"rivers = QgsProject.instance().mapLayersByName(\"rivers\")[0]\nriver_sites = processing.run(\"native:randompointsonlines\", {\n    \"INPUT\": rivers, \"POINTS_NUMBER\": 3, \"MIN_DISTANCE\": 200,\n    \"SEED\": 7, \"INCLUDE_LINE_ATTRIBUTES\": True,\n    \"OUTPUT\": \"memory:river_sites\"})[\"OUTPUT\"]\n\nextent_points = processing.run(\"native:randompointsinextent\", {\n    \"EXTENT\": stands.extent(), \"POINTS_NUMBER\": 500, \"MIN_DISTANCE\": 0,\n    \"TARGET_CRS\": stands.crs(), \"SEED\": 7,\n    \"OUTPUT\": \"memory:extent_points\"})[\"OUTPUT\"]\n",[141,432,433,451,465,493,513,529,533,547,572,589],{"__ignoreMap":151},[155,434,435,438,440,442,445,447,449],{"class":157,"line":158},[155,436,437],{"class":165},"rivers ",[155,439,196],{"class":161},[155,441,199],{"class":165},[155,443,444],{"class":202},"\"rivers\"",[155,446,206],{"class":165},[155,448,46],{"class":209},[155,450,212],{"class":165},[155,452,453,456,458,460,463],{"class":157,"line":169},[155,454,455],{"class":165},"river_sites ",[155,457,196],{"class":161},[155,459,223],{"class":165},[155,461,462],{"class":202},"\"native:randompointsonlines\"",[155,464,229],{"class":165},[155,466,467,469,472,475,477,480,483,486,488,491],{"class":157,"line":183},[155,468,235],{"class":202},[155,470,471],{"class":165},": rivers, ",[155,473,474],{"class":202},"\"POINTS_NUMBER\"",[155,476,247],{"class":165},[155,478,479],{"class":209},"3",[155,481,482],{"class":165},", ",[155,484,485],{"class":202},"\"MIN_DISTANCE\"",[155,487,247],{"class":165},[155,489,490],{"class":209},"200",[155,492,253],{"class":165},[155,494,495,497,499,502,504,507,509,511],{"class":157,"line":190},[155,496,301],{"class":202},[155,498,247],{"class":165},[155,500,501],{"class":209},"7",[155,503,482],{"class":165},[155,505,506],{"class":202},"\"INCLUDE_LINE_ATTRIBUTES\"",[155,508,247],{"class":165},[155,510,319],{"class":209},[155,512,253],{"class":165},[155,514,515,517,519,522,525,527],{"class":157,"line":215},[155,516,327],{"class":202},[155,518,247],{"class":165},[155,520,521],{"class":202},"\"memory:river_sites\"",[155,523,524],{"class":165},"})[",[155,526,354],{"class":202},[155,528,212],{"class":165},[155,530,531],{"class":157,"line":232},[155,532,187],{"emptyLinePlaceholder":186},[155,534,535,538,540,542,545],{"class":157,"line":241},[155,536,537],{"class":165},"extent_points ",[155,539,196],{"class":161},[155,541,223],{"class":165},[155,543,544],{"class":202},"\"native:randompointsinextent\"",[155,546,229],{"class":165},[155,548,549,552,555,557,559,562,564,566,568,570],{"class":157,"line":256},[155,550,551],{"class":202},"    \"EXTENT\"",[155,553,554],{"class":165},": stands.extent(), ",[155,556,474],{"class":202},[155,558,247],{"class":165},[155,560,561],{"class":209},"500",[155,563,482],{"class":165},[155,565,485],{"class":202},[155,567,247],{"class":165},[155,569,46],{"class":209},[155,571,253],{"class":165},[155,573,574,577,580,583,585,587],{"class":157,"line":274},[155,575,576],{"class":202},"    \"TARGET_CRS\"",[155,578,579],{"class":165},": stands.crs(), ",[155,581,582],{"class":202},"\"SEED\"",[155,584,247],{"class":165},[155,586,501],{"class":209},[155,588,253],{"class":165},[155,590,591,593,595,598,600,602],{"class":157,"line":286},[155,592,327],{"class":202},[155,594,247],{"class":165},[155,596,597],{"class":202},"\"memory:extent_points\"",[155,599,524],{"class":165},[155,601,354],{"class":202},[155,603,212],{"class":165},[14,605,606,608,609,612],{},[393,607,395],{}," Points on lines are placed uniformly along length, so a long river gets the same number of points as a short stream when the count is per feature — use a data-defined count based on ",[141,610,611],{},"$length"," for proportional sampling. Random points in an extent fill a rectangle and ignore where features are; they are mostly useful as a baseline or for synthetic tests. Both accept seeds.",[116,614,616],{"id":615},"enforce-spacing-without-losing-points","Enforce spacing without losing points",[14,618,619],{},"Minimum spacing prevents plots from clumping, which matters for field logistics and for statistical independence. Too tight a spacing in small polygons, however, means points cannot be placed.",[14,621,622],{},[29,623,626,629,632,635,638,648,654,658,661,664,668,673,678,682,685,689,694,699,703,707],{"viewBox":624,"role":32,"ariaLabel":625,"xmlns":34},"0 0 760 250","Five points fit in a large polygon at the chosen spacing while a small polygon fits only two, with options to fix the shortfall",[36,627,628],{},"Spacing versus polygon size",[40,630,631],{},"A large polygon easily fits five points fifty metres apart. A small polygon only fits two at that spacing, so three points are missed. Options: reduce the spacing, lower the count for small polygons with a data-defined expression, or merge small polygons into larger strata before sampling.",[44,633],{"x":46,"y":46,"width":47,"height":634,"fill":49},"250",[51,636,637],{"x":53,"y":54,"style":55,"fill":56,"textAnchor":57},"Spacing must fit the smallest polygon",[44,639],{"x":640,"y":641,"width":642,"height":643,"rx":644,"fill":645,"stroke":646,"style":647},"40","60","260","150","6","#e8efe6","#15803d","stroke-width:1.5",[649,650],"circle",{"cx":651,"cy":652,"r":644,"fill":646,"stroke":646,"style":653},"80","100","stroke-width:1",[649,655],{"cx":656,"cy":657,"r":644,"fill":646,"stroke":646,"style":653},"160","90",[649,659],{"cx":634,"cy":660,"r":644,"fill":646,"stroke":646,"style":653},"110",[649,662],{"cx":660,"cy":663,"r":644,"fill":646,"stroke":646,"style":653},"170",[649,665],{"cx":666,"cy":667,"r":644,"fill":646,"stroke":646,"style":653},"220","180",[51,669,672],{"x":663,"y":670,"style":78,"fill":671,"textAnchor":57},"230","#166534","large stand: 5 of 5",[44,674],{"x":63,"y":652,"width":657,"height":675,"rx":644,"fill":676,"stroke":677,"style":647},"70","#fdf2e2","#b45309",[649,679],{"cx":680,"cy":681,"r":644,"fill":677,"stroke":677,"style":653},"365","125",[649,683],{"cx":684,"cy":643,"r":644,"fill":677,"stroke":677,"style":653},"405",[51,686,688],{"x":687,"y":670,"style":78,"fill":677,"textAnchor":57},"385","small: 2 of 5",[44,690],{"x":691,"y":641,"width":692,"height":643,"rx":65,"fill":693,"stroke":92,"style":68},"470","266","#fffdf7",[51,695,698],{"x":696,"y":697,"style":73,"fill":56,"textAnchor":57},"603","96.78","fixes",[51,700,702],{"x":696,"y":701,"style":78,"fill":79,"textAnchor":57},"124.78","lower MIN_DISTANCE",[51,704,706],{"x":696,"y":705,"style":78,"fill":79,"textAnchor":57},"152.78","count from $area",[51,708,710],{"x":696,"y":709,"style":78,"fill":79,"textAnchor":57},"180.78","merge small strata",[146,712,714],{"className":148,"code":713,"language":150,"meta":151,"style":151},"from qgis.core import QgsProperty, QgsProcessingFeatureSourceDefinition\n\nparams = {\n    \"INPUT\": stands,\n    \"POINTS_NUMBER\": QgsProperty.fromExpression(\"max(1, min(5, floor($area \u002F 20000)))\"),\n    \"MIN_DISTANCE\": 50, \"SEED\": 20261002, \"INCLUDE_POLYGON_ATTRIBUTES\": True,\n    \"OUTPUT\": \"memory:survey_plots_scaled\"}\nres = processing.run(\"native:randompointsinpolygons\", params)\nprint(res[\"POINTS_MISSED\"], \"missed with area-scaled counts\")\n",[141,715,716,727,731,741,747,760,787,799,813],{"__ignoreMap":151},[155,717,718,720,722,724],{"class":157,"line":158},[155,719,172],{"class":161},[155,721,175],{"class":165},[155,723,162],{"class":161},[155,725,726],{"class":165}," QgsProperty, QgsProcessingFeatureSourceDefinition\n",[155,728,729],{"class":157,"line":169},[155,730,187],{"emptyLinePlaceholder":186},[155,732,733,736,738],{"class":157,"line":183},[155,734,735],{"class":165},"params ",[155,737,196],{"class":161},[155,739,740],{"class":165}," {\n",[155,742,743,745],{"class":157,"line":190},[155,744,235],{"class":202},[155,746,238],{"class":165},[155,748,749,751,754,757],{"class":157,"line":215},[155,750,244],{"class":202},[155,752,753],{"class":165},": QgsProperty.fromExpression(",[155,755,756],{"class":202},"\"max(1, min(5, floor($area \u002F 20000)))\"",[155,758,759],{"class":165},"),\n",[155,761,762,764,766,768,770,772,774,776,778,781,783,785],{"class":157,"line":232},[155,763,259],{"class":202},[155,765,247],{"class":165},[155,767,264],{"class":209},[155,769,482],{"class":165},[155,771,582],{"class":202},[155,773,247],{"class":165},[155,775,306],{"class":209},[155,777,482],{"class":165},[155,779,780],{"class":202},"\"INCLUDE_POLYGON_ATTRIBUTES\"",[155,782,247],{"class":165},[155,784,319],{"class":209},[155,786,253],{"class":165},[155,788,789,791,793,796],{"class":157,"line":241},[155,790,327],{"class":202},[155,792,247],{"class":165},[155,794,795],{"class":202},"\"memory:survey_plots_scaled\"",[155,797,798],{"class":165},"}\n",[155,800,801,804,806,808,810],{"class":157,"line":256},[155,802,803],{"class":165},"res ",[155,805,196],{"class":161},[155,807,223],{"class":165},[155,809,226],{"class":202},[155,811,812],{"class":165},", params)\n",[155,814,815,817,820,822,824,827],{"class":157,"line":274},[155,816,362],{"class":209},[155,818,819],{"class":165},"(res[",[155,821,374],{"class":202},[155,823,377],{"class":165},[155,825,826],{"class":202},"\"missed with area-scaled counts\"",[155,828,383],{"class":165},[14,830,831,833,834,837],{},[393,832,395],{}," Passing a ",[141,835,836],{},"QgsProperty"," instead of a number makes the count data-defined: here one point per two hectares, between one and five per stand. Scaling counts by area keeps sampling intensity even across stands and almost always eliminates missed points. If some stands are still too small, the honest choice is to report them as unsampled or to merge them with neighbours of the same type, rather than silently dropping the spacing rule.",[116,839,841],{"id":840},"sample-existing-features-at-random","Sample existing features at random",[14,843,844],{},"When the population already exists — addresses to survey, parcels to audit, assets to inspect — the task is to pick a random subset rather than generate locations.",[146,846,848],{"className":148,"code":847,"language":150,"meta":151,"style":151},"addresses = QgsProject.instance().mapLayersByName(\"addresses\")[0]\nsample = processing.run(\"native:randomextract\", {\n    \"INPUT\": addresses, \"METHOD\": 0,       # 0 = number of features, 1 = percentage\n    \"NUMBER\": 400, \"OUTPUT\": \"memory:address_sample\"})[\"OUTPUT\"]\nprint(sample.featureCount(), \"addresses sampled\")\n",[141,849,850,868,882,902,927],{"__ignoreMap":151},[155,851,852,855,857,859,862,864,866],{"class":157,"line":158},[155,853,854],{"class":165},"addresses ",[155,856,196],{"class":161},[155,858,199],{"class":165},[155,860,861],{"class":202},"\"addresses\"",[155,863,206],{"class":165},[155,865,46],{"class":209},[155,867,212],{"class":165},[155,869,870,873,875,877,880],{"class":157,"line":169},[155,871,872],{"class":165},"sample ",[155,874,196],{"class":161},[155,876,223],{"class":165},[155,878,879],{"class":202},"\"native:randomextract\"",[155,881,229],{"class":165},[155,883,884,886,889,892,894,896,899],{"class":157,"line":183},[155,885,235],{"class":202},[155,887,888],{"class":165},": addresses, ",[155,890,891],{"class":202},"\"METHOD\"",[155,893,247],{"class":165},[155,895,46],{"class":209},[155,897,898],{"class":165},",       ",[155,900,901],{"class":270},"# 0 = number of features, 1 = percentage\n",[155,903,904,907,909,912,914,916,918,921,923,925],{"class":157,"line":190},[155,905,906],{"class":202},"    \"NUMBER\"",[155,908,247],{"class":165},[155,910,911],{"class":209},"400",[155,913,482],{"class":165},[155,915,354],{"class":202},[155,917,247],{"class":165},[155,919,920],{"class":202},"\"memory:address_sample\"",[155,922,524],{"class":165},[155,924,354],{"class":202},[155,926,212],{"class":165},[155,928,929,931,934,937],{"class":157,"line":215},[155,930,362],{"class":209},[155,932,933],{"class":165},"(sample.featureCount(), ",[155,935,936],{"class":202},"\"addresses sampled\"",[155,938,383],{"class":165},[14,940,941,396,943,946,947,950],{},[393,942,395],{},[141,944,945],{},"native:randomextract"," returns a new layer with the chosen features and all their attributes, which is what a survey team needs. ",[141,948,949],{},"METHOD: 1"," takes a percentage instead. Unlike the point generators, this algorithm has no seed parameter, so a repeatable sample needs Python's own random module, shown below.",[116,952,954],{"id":953},"stratified-samples","Stratified samples",[14,956,957],{},"A simple random sample of 400 addresses may contain three from a small district and ninety from a large one. Stratified sampling draws a fixed number — or a fixed share — from each stratum, guaranteeing coverage.",[14,959,960],{},[29,961,964,967,970,972,975,982,986,988,991,996,1000,1002,1005,1007,1010,1016,1022],{"viewBox":962,"role":32,"ariaLabel":963,"xmlns":34},"0 0 760 230","Bar comparison of simple random sampling, which under-samples a small district, and stratified sampling, which takes the same number from each",[36,965,966],{},"Simple versus stratified sampling",[40,968,969],{},"Simple random sampling of 400 addresses from four districts of very different size gives counts roughly proportional to size, with only a handful from the smallest district. Stratified sampling draws 100 from each district, guaranteeing enough responses everywhere at the cost of over-representing small districts, which weights then correct.",[44,971],{"x":46,"y":46,"width":47,"height":670,"fill":49},[51,973,974],{"x":53,"y":54,"style":55,"fill":56,"textAnchor":57},"400 addresses from four districts",[51,976,981],{"x":977,"y":978,"style":979,"fill":79,"textAnchor":980},"120","74","text-anchor:end;font-size:11.0px;font-family:sans-serif","end","simple",[44,983],{"x":984,"y":641,"width":634,"height":985,"rx":479,"fill":98,"stroke":98,"style":647},"130","22",[44,987],{"x":53,"y":641,"width":984,"height":985,"rx":479,"fill":67,"stroke":67,"style":647},[44,989],{"x":990,"y":641,"width":652,"height":985,"rx":479,"fill":677,"stroke":677,"style":647},"510",[44,992],{"x":993,"y":641,"width":994,"height":985,"rx":479,"fill":995,"stroke":995,"style":647},"610","20","#b91c1c",[51,997,999],{"x":977,"y":998,"style":979,"fill":79,"textAnchor":980},"134","stratified",[44,1001],{"x":984,"y":977,"width":681,"height":985,"rx":479,"fill":98,"stroke":98,"style":647},[44,1003],{"x":1004,"y":977,"width":681,"height":985,"rx":479,"fill":67,"stroke":67,"style":647},"255",[44,1006],{"x":53,"y":977,"width":681,"height":985,"rx":479,"fill":677,"stroke":677,"style":647},[44,1008],{"x":1009,"y":977,"width":681,"height":985,"rx":479,"fill":995,"stroke":995,"style":647},"505",[51,1011,1015],{"x":1012,"y":1013,"style":1014,"fill":995,"textAnchor":980},"620","104","text-anchor:end;font-size:10.0px;font-family:sans-serif","8 from the small district",[51,1017,1021],{"x":1018,"y":1019,"style":1020,"fill":92,"textAnchor":57},"568","164","text-anchor:middle;font-size:10.0px;font-family:sans-serif","100 each",[51,1023,1025],{"x":53,"y":1024,"style":78,"fill":92,"textAnchor":57},"206","stratify for coverage, then weight results back to population shares",[146,1027,1029],{"className":148,"code":1028,"language":150,"meta":151,"style":151},"import random\nfrom collections import defaultdict\nfrom qgis.core import QgsFeatureRequest\n\nrng = random.Random(20261002)\nby_district = defaultdict(list)\nfor f in addresses.getFeatures(QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry)):\n    by_district[f[\"district\"]].append(f.id())\n\nPER_STRATUM = 100\nchosen, weights = [], {}\nfor district, ids in sorted(by_district.items()):\n    k = min(PER_STRATUM, len(ids))\n    picked = rng.sample(sorted(ids), k)\n    chosen.extend(picked)\n    weights[district] = len(ids) \u002F k            # each response represents this many\n\nstratified = addresses.materialize(QgsFeatureRequest().setFilterFids(chosen))\nstratified.setName(\"address sample (stratified)\")\nQgsProject.instance().addMapLayer(stratified)\nprint({d: round(w, 1) for d, w in weights.items()})\n",[141,1030,1031,1038,1050,1061,1065,1079,1094,1108,1119,1123,1134,1144,1159,1182,1198,1203,1225,1229,1240,1251,1257],{"__ignoreMap":151},[155,1032,1033,1035],{"class":157,"line":158},[155,1034,162],{"class":161},[155,1036,1037],{"class":165}," random\n",[155,1039,1040,1042,1045,1047],{"class":157,"line":169},[155,1041,172],{"class":161},[155,1043,1044],{"class":165}," collections ",[155,1046,162],{"class":161},[155,1048,1049],{"class":165}," defaultdict\n",[155,1051,1052,1054,1056,1058],{"class":157,"line":183},[155,1053,172],{"class":161},[155,1055,175],{"class":165},[155,1057,162],{"class":161},[155,1059,1060],{"class":165}," QgsFeatureRequest\n",[155,1062,1063],{"class":157,"line":190},[155,1064,187],{"emptyLinePlaceholder":186},[155,1066,1067,1070,1072,1075,1077],{"class":157,"line":215},[155,1068,1069],{"class":165},"rng ",[155,1071,196],{"class":161},[155,1073,1074],{"class":165}," random.Random(",[155,1076,306],{"class":209},[155,1078,383],{"class":165},[155,1080,1081,1084,1086,1089,1092],{"class":157,"line":232},[155,1082,1083],{"class":165},"by_district ",[155,1085,196],{"class":161},[155,1087,1088],{"class":165}," defaultdict(",[155,1090,1091],{"class":209},"list",[155,1093,383],{"class":165},[155,1095,1096,1099,1102,1105],{"class":157,"line":241},[155,1097,1098],{"class":161},"for",[155,1100,1101],{"class":165}," f ",[155,1103,1104],{"class":161},"in",[155,1106,1107],{"class":165}," addresses.getFeatures(QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry)):\n",[155,1109,1110,1113,1116],{"class":157,"line":256},[155,1111,1112],{"class":165},"    by_district[f[",[155,1114,1115],{"class":202},"\"district\"",[155,1117,1118],{"class":165},"]].append(f.id())\n",[155,1120,1121],{"class":157,"line":274},[155,1122,187],{"emptyLinePlaceholder":186},[155,1124,1125,1128,1131],{"class":157,"line":286},[155,1126,1127],{"class":209},"PER_STRATUM",[155,1129,1130],{"class":161}," =",[155,1132,1133],{"class":209}," 100\n",[155,1135,1136,1139,1141],{"class":157,"line":298},[155,1137,1138],{"class":165},"chosen, weights ",[155,1140,196],{"class":161},[155,1142,1143],{"class":165}," [], {}\n",[155,1145,1146,1148,1151,1153,1156],{"class":157,"line":311},[155,1147,1098],{"class":161},[155,1149,1150],{"class":165}," district, ids ",[155,1152,1104],{"class":161},[155,1154,1155],{"class":209}," sorted",[155,1157,1158],{"class":165},"(by_district.items()):\n",[155,1160,1161,1164,1166,1169,1172,1174,1176,1179],{"class":157,"line":324},[155,1162,1163],{"class":165},"    k ",[155,1165,196],{"class":161},[155,1167,1168],{"class":209}," min",[155,1170,1171],{"class":165},"(",[155,1173,1127],{"class":209},[155,1175,482],{"class":165},[155,1177,1178],{"class":209},"len",[155,1180,1181],{"class":165},"(ids))\n",[155,1183,1184,1187,1189,1192,1195],{"class":157,"line":337},[155,1185,1186],{"class":165},"    picked ",[155,1188,196],{"class":161},[155,1190,1191],{"class":165}," rng.sample(",[155,1193,1194],{"class":209},"sorted",[155,1196,1197],{"class":165},"(ids), k)\n",[155,1199,1200],{"class":157,"line":343},[155,1201,1202],{"class":165},"    chosen.extend(picked)\n",[155,1204,1205,1208,1210,1213,1216,1219,1222],{"class":157,"line":359},[155,1206,1207],{"class":165},"    weights[district] ",[155,1209,196],{"class":161},[155,1211,1212],{"class":209}," len",[155,1214,1215],{"class":165},"(ids) ",[155,1217,1218],{"class":161},"\u002F",[155,1220,1221],{"class":165}," k            ",[155,1223,1224],{"class":270},"# each response represents this many\n",[155,1226,1227],{"class":157,"line":386},[155,1228,187],{"emptyLinePlaceholder":186},[155,1230,1232,1235,1237],{"class":157,"line":1231},18,[155,1233,1234],{"class":165},"stratified ",[155,1236,196],{"class":161},[155,1238,1239],{"class":165}," addresses.materialize(QgsFeatureRequest().setFilterFids(chosen))\n",[155,1241,1243,1246,1249],{"class":157,"line":1242},19,[155,1244,1245],{"class":165},"stratified.setName(",[155,1247,1248],{"class":202},"\"address sample (stratified)\"",[155,1250,383],{"class":165},[155,1252,1254],{"class":157,"line":1253},20,[155,1255,1256],{"class":165},"QgsProject.instance().addMapLayer(stratified)\n",[155,1258,1260,1262,1265,1268,1271,1274,1277,1279,1282,1284],{"class":157,"line":1259},21,[155,1261,362],{"class":209},[155,1263,1264],{"class":165},"({d: ",[155,1266,1267],{"class":209},"round",[155,1269,1270],{"class":165},"(w, ",[155,1272,1273],{"class":209},"1",[155,1275,1276],{"class":165},") ",[155,1278,1098],{"class":161},[155,1280,1281],{"class":165}," d, w ",[155,1283,1104],{"class":161},[155,1285,1286],{"class":165}," weights.items()})\n",[14,1288,1289,1291,1292,1295],{},[393,1290,395],{}," A dedicated ",[141,1293,1294],{},"random.Random"," instance with a fixed seed makes the sample reproducible without affecting any other code that uses the random module. Sorting ids before sampling matters: without it, the result depends on iteration order, which can differ between providers or runs. The design weight per stratum — population divided by sample size — is what you later multiply responses by to estimate totals for the whole area, correcting for the deliberate over-representation of small districts.",[116,1297,1299],{"id":1298},"use-random-points-as-a-baseline","Use random points as a baseline",[14,1301,1302],{},"Random points in the same area are the reference against which an observed pattern is judged. A simple Monte Carlo test repeats the random placement many times and asks how often chance produces a value as extreme as the observed one.",[146,1304,1306],{"className":148,"code":1305,"language":150,"meta":151,"style":151},"from qgis.core import QgsSpatialIndex\n\ndef mean_nn(layer):\n    geoms = {f.id(): f.geometry() for f in layer.getFeatures()}\n    index = QgsSpatialIndex(layer.getFeatures())\n    total = 0.0\n    for fid, g in geoms.items():\n        other = [n for n in index.nearestNeighbor(g.asPoint(), 2) if n != fid][0]\n        total += g.distance(geoms[other])\n    return total \u002F len(geoms)\n\nobserved_layer = QgsProject.instance().mapLayersByName(\"cafes\")[0]\ncity = QgsProject.instance().mapLayersByName(\"city_boundary\")[0]\nobserved = mean_nn(observed_layer)\nn = observed_layer.featureCount()\n\nsims = []\nfor seed in range(99):\n    r = processing.run(\"native:randompointsinpolygons\", {\n        \"INPUT\": city, \"POINTS_NUMBER\": n, \"SEED\": seed + 1,\n        \"OUTPUT\": \"memory:\"})[\"OUTPUT\"]\n    sims.append(mean_nn(r))\nrank = sum(1 for s in sims if s \u003C= observed)\nprint(f\"observed {observed:.0f} m; {rank} of 99 random runs were as clustered (p ≈ {(rank + 1) \u002F 100:.2f})\")\n",[141,1307,1308,1319,1323,1335,1354,1364,1374,1387,1427,1438,1453,1457,1475,1493,1503,1513,1517,1527,1547,1560,1586,1602,1608,1644],{"__ignoreMap":151},[155,1309,1310,1312,1314,1316],{"class":157,"line":158},[155,1311,172],{"class":161},[155,1313,175],{"class":165},[155,1315,162],{"class":161},[155,1317,1318],{"class":165}," QgsSpatialIndex\n",[155,1320,1321],{"class":157,"line":169},[155,1322,187],{"emptyLinePlaceholder":186},[155,1324,1325,1328,1332],{"class":157,"line":183},[155,1326,1327],{"class":161},"def",[155,1329,1331],{"class":1330},"svObZ"," mean_nn",[155,1333,1334],{"class":165},"(layer):\n",[155,1336,1337,1340,1342,1345,1347,1349,1351],{"class":157,"line":190},[155,1338,1339],{"class":165},"    geoms ",[155,1341,196],{"class":161},[155,1343,1344],{"class":165}," {f.id(): f.geometry() ",[155,1346,1098],{"class":161},[155,1348,1101],{"class":165},[155,1350,1104],{"class":161},[155,1352,1353],{"class":165}," layer.getFeatures()}\n",[155,1355,1356,1359,1361],{"class":157,"line":215},[155,1357,1358],{"class":165},"    index ",[155,1360,196],{"class":161},[155,1362,1363],{"class":165}," QgsSpatialIndex(layer.getFeatures())\n",[155,1365,1366,1369,1371],{"class":157,"line":232},[155,1367,1368],{"class":165},"    total ",[155,1370,196],{"class":161},[155,1372,1373],{"class":209}," 0.0\n",[155,1375,1376,1379,1382,1384],{"class":157,"line":241},[155,1377,1378],{"class":161},"    for",[155,1380,1381],{"class":165}," fid, g ",[155,1383,1104],{"class":161},[155,1385,1386],{"class":165}," geoms.items():\n",[155,1388,1389,1392,1394,1397,1399,1402,1404,1407,1410,1412,1415,1417,1420,1423,1425],{"class":157,"line":256},[155,1390,1391],{"class":165},"        other ",[155,1393,196],{"class":161},[155,1395,1396],{"class":165}," [n ",[155,1398,1098],{"class":161},[155,1400,1401],{"class":165}," n ",[155,1403,1104],{"class":161},[155,1405,1406],{"class":165}," index.nearestNeighbor(g.asPoint(), ",[155,1408,1409],{"class":209},"2",[155,1411,1276],{"class":165},[155,1413,1414],{"class":161},"if",[155,1416,1401],{"class":165},[155,1418,1419],{"class":161},"!=",[155,1421,1422],{"class":165}," fid][",[155,1424,46],{"class":209},[155,1426,212],{"class":165},[155,1428,1429,1432,1435],{"class":157,"line":274},[155,1430,1431],{"class":165},"        total ",[155,1433,1434],{"class":161},"+=",[155,1436,1437],{"class":165}," g.distance(geoms[other])\n",[155,1439,1440,1443,1446,1448,1450],{"class":157,"line":286},[155,1441,1442],{"class":161},"    return",[155,1444,1445],{"class":165}," total ",[155,1447,1218],{"class":161},[155,1449,1212],{"class":209},[155,1451,1452],{"class":165},"(geoms)\n",[155,1454,1455],{"class":157,"line":298},[155,1456,187],{"emptyLinePlaceholder":186},[155,1458,1459,1462,1464,1466,1469,1471,1473],{"class":157,"line":311},[155,1460,1461],{"class":165},"observed_layer ",[155,1463,196],{"class":161},[155,1465,199],{"class":165},[155,1467,1468],{"class":202},"\"cafes\"",[155,1470,206],{"class":165},[155,1472,46],{"class":209},[155,1474,212],{"class":165},[155,1476,1477,1480,1482,1484,1487,1489,1491],{"class":157,"line":324},[155,1478,1479],{"class":165},"city ",[155,1481,196],{"class":161},[155,1483,199],{"class":165},[155,1485,1486],{"class":202},"\"city_boundary\"",[155,1488,206],{"class":165},[155,1490,46],{"class":209},[155,1492,212],{"class":165},[155,1494,1495,1498,1500],{"class":157,"line":337},[155,1496,1497],{"class":165},"observed ",[155,1499,196],{"class":161},[155,1501,1502],{"class":165}," mean_nn(observed_layer)\n",[155,1504,1505,1508,1510],{"class":157,"line":343},[155,1506,1507],{"class":165},"n ",[155,1509,196],{"class":161},[155,1511,1512],{"class":165}," observed_layer.featureCount()\n",[155,1514,1515],{"class":157,"line":359},[155,1516,187],{"emptyLinePlaceholder":186},[155,1518,1519,1522,1524],{"class":157,"line":386},[155,1520,1521],{"class":165},"sims ",[155,1523,196],{"class":161},[155,1525,1526],{"class":165}," []\n",[155,1528,1529,1531,1534,1536,1539,1541,1544],{"class":157,"line":1231},[155,1530,1098],{"class":161},[155,1532,1533],{"class":165}," seed ",[155,1535,1104],{"class":161},[155,1537,1538],{"class":209}," range",[155,1540,1171],{"class":165},[155,1542,1543],{"class":209},"99",[155,1545,1546],{"class":165},"):\n",[155,1548,1549,1552,1554,1556,1558],{"class":157,"line":1242},[155,1550,1551],{"class":165},"    r ",[155,1553,196],{"class":161},[155,1555,223],{"class":165},[155,1557,226],{"class":202},[155,1559,229],{"class":165},[155,1561,1562,1565,1568,1570,1573,1575,1578,1581,1584],{"class":157,"line":1253},[155,1563,1564],{"class":202},"        \"INPUT\"",[155,1566,1567],{"class":165},": city, ",[155,1569,474],{"class":202},[155,1571,1572],{"class":165},": n, ",[155,1574,582],{"class":202},[155,1576,1577],{"class":165},": seed ",[155,1579,1580],{"class":161},"+",[155,1582,1583],{"class":209}," 1",[155,1585,253],{"class":165},[155,1587,1588,1591,1593,1596,1598,1600],{"class":157,"line":1259},[155,1589,1590],{"class":202},"        \"OUTPUT\"",[155,1592,247],{"class":165},[155,1594,1595],{"class":202},"\"memory:\"",[155,1597,524],{"class":165},[155,1599,354],{"class":202},[155,1601,212],{"class":165},[155,1603,1605],{"class":157,"line":1604},22,[155,1606,1607],{"class":165},"    sims.append(mean_nn(r))\n",[155,1609,1611,1614,1616,1619,1621,1623,1626,1629,1631,1634,1636,1638,1641],{"class":157,"line":1610},23,[155,1612,1613],{"class":165},"rank ",[155,1615,196],{"class":161},[155,1617,1618],{"class":209}," sum",[155,1620,1171],{"class":165},[155,1622,1273],{"class":209},[155,1624,1625],{"class":161}," for",[155,1627,1628],{"class":165}," s ",[155,1630,1104],{"class":161},[155,1632,1633],{"class":165}," sims ",[155,1635,1414],{"class":161},[155,1637,1628],{"class":165},[155,1639,1640],{"class":161},"\u003C=",[155,1642,1643],{"class":165}," observed)\n",[155,1645,1647,1649,1651,1654,1657,1660,1663,1666,1669,1672,1674,1677,1679,1682,1684,1687,1689,1691,1693,1695,1698,1701,1703,1706],{"class":157,"line":1646},24,[155,1648,362],{"class":209},[155,1650,1171],{"class":165},[155,1652,1653],{"class":161},"f",[155,1655,1656],{"class":202},"\"observed ",[155,1658,1659],{"class":209},"{",[155,1661,1662],{"class":165},"observed",[155,1664,1665],{"class":161},":.0f",[155,1667,1668],{"class":209},"}",[155,1670,1671],{"class":202}," m; ",[155,1673,1659],{"class":209},[155,1675,1676],{"class":165},"rank",[155,1678,1668],{"class":209},[155,1680,1681],{"class":202}," of 99 random runs were as clustered (p ≈ ",[155,1683,1659],{"class":209},[155,1685,1686],{"class":165},"(rank ",[155,1688,1580],{"class":161},[155,1690,1583],{"class":209},[155,1692,1276],{"class":165},[155,1694,1218],{"class":161},[155,1696,1697],{"class":209}," 100",[155,1699,1700],{"class":161},":.2f",[155,1702,1668],{"class":209},[155,1704,1705],{"class":202},")\"",[155,1707,383],{"class":165},[14,1709,1710,1712,1713,1717],{},[393,1711,395],{}," Each simulation places the same number of points at random inside the same city boundary and computes the same statistic. If the observed mean nearest distance is smaller than all 99 simulated ones, the pattern is more clustered than chance at roughly the 1 % level. This avoids the formula assumptions of the ",[21,1714,1716],{"href":1715},"\u002Fspatial-data-processing-automation\u002Fspatial-statistics-and-pattern-analysis\u002Fnearest-neighbour-analysis-pyqgis\u002F","nearest neighbour index"," — irregular boundaries and edge effects are automatically accounted for, because the simulations experience them too. Using the seed as the loop variable makes the whole test reproducible.",[116,1719,1721],{"id":1720},"qgis-version-compatibility","QGIS version compatibility",[14,1723,1724,1726,1727,1730,1731,1733,1734,1737,1738,1726,1740,1743,1744,1747],{},[141,1725,143],{}," and ",[141,1728,1729],{},"native:randompointsonlines"," with seeds and ",[141,1732,419],{}," arrived in QGIS 3.22 and are present on 3.34 LTR, 3.40 LTR and QGIS 4; older releases had ",[141,1735,1736],{},"qgis:randompointsinsidepolygons"," without a seed. ",[141,1739,945],{},[141,1741,1742],{},"native:randompointsinextent"," are available throughout. Check ",[141,1745,1746],{},"processing.algorithmHelp(...)"," for exact parameter names on your version.",[116,1749,1751],{"id":1750},"troubleshooting","Troubleshooting",[121,1753,1754,1760,1770,1776],{},[124,1755,1756,1759],{},[393,1757,1758],{},"Many points missed."," Minimum distance is too large for small polygons; scale counts by area.",[124,1761,1762,1765,1766,1769],{},[393,1763,1764],{},"The sample changes every run."," No seed was set, or ids were not sorted before ",[141,1767,1768],{},"rng.sample",".",[124,1771,1772,1775],{},[393,1773,1774],{},"Points appear in lakes."," The polygon layer includes water; subtract it before sampling.",[124,1777,1778,1781],{},[393,1779,1780],{},"Line points cluster on long features."," Counts are per feature; use a length-based expression.",[116,1783,1785],{"id":1784},"conclusion","Conclusion",[14,1787,1788,1789,1791],{},"Generate random locations with the native algorithms and a seed, scale counts by area or length and keep minimum distances realistic, sample existing features with ",[141,1790,945],{}," or a seeded Python sampler, stratify when coverage matters and record design weights, and use repeated random placements as the baseline for honest pattern tests.",[116,1793,1795],{"id":1794},"frequently-asked-questions","Frequently Asked Questions",[14,1797,1798,1801],{},[393,1799,1800],{},"Is a regular grid better than random points for surveys?","\nA grid gives even coverage and is easy to navigate; random points avoid alignment with regular features such as tree rows. Systematic sampling with a random start combines both.",[14,1803,1804,1807],{},[393,1805,1806],{},"Can I sample raster cells at random?","\nGenerate random points in the raster extent and sample values at them, or read pixels into NumPy and sample indices.",[14,1809,1810,1813],{},[393,1811,1812],{},"How many simulations does a Monte Carlo test need?","\n99 gives a resolution of 1 %; 999 gives 0.1 % at ten times the cost.",[14,1815,1816,1819],{},[393,1817,1818],{},"Does QGIS's random generator give the same points on every platform?","\nFor a given seed and version, yes in practice; across QGIS versions the implementation may change, so record the version with the seed.",[116,1821,1823],{"id":1822},"related","Related",[121,1825,1826,1831,1836,1842,1848],{},[124,1827,1828,1830],{},[21,1829,24],{"href":23}," — the guide this recipe belongs to",[124,1832,1833],{},[21,1834,1835],{"href":1715},"Run a Nearest Neighbour Analysis in PyQGIS",[124,1837,1838],{},[21,1839,1841],{"href":1840},"\u002Fspatial-data-processing-automation\u002Fspatial-statistics-and-pattern-analysis\u002Fcluster-points-with-dbscan-and-kmeans-pyqgis\u002F","Cluster Points with DBSCAN and K-Means in PyQGIS",[124,1843,1844],{},[21,1845,1847],{"href":1846},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fcreate-hexagon-grid-and-count-points-pyqgis\u002F","Create a Hexagon Grid and Count Points in PyQGIS",[124,1849,1850],{},[21,1851,1853],{"href":1852},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fsplit-vector-layer-by-attribute-pyqgis\u002F","Split a Vector Layer by Attribute in PyQGIS",[1855,1856,1857],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sjoCn, html code.shiki .sjoCn{--shiki-default:#9AA79F}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}",{"title":151,"searchDepth":169,"depth":169,"links":1859},[1860,1861,1862,1863,1864,1865,1866,1867,1868,1869,1870,1871],{"id":118,"depth":169,"text":119},{"id":135,"depth":169,"text":136},{"id":423,"depth":169,"text":424},{"id":615,"depth":169,"text":616},{"id":840,"depth":169,"text":841},{"id":953,"depth":169,"text":954},{"id":1298,"depth":169,"text":1299},{"id":1720,"depth":169,"text":1721},{"id":1750,"depth":169,"text":1751},{"id":1784,"depth":169,"text":1785},{"id":1794,"depth":169,"text":1795},{"id":1822,"depth":169,"text":1823},"Create random points inside polygons, along lines and within an extent, draw random and stratified samples of features, enforce minimum spacing, make results reproducible with seeds, and build the random baselines that pattern tests compare against.","md",{"slug":1875,"type":1876,"breadcrumb":1877,"datePublished":1878,"dateModified":1878},"generate-random-points-and-sampling-pyqgis","article","Random Points and Spatial Samples","2026-10-02","\u002Fspatial-data-processing-automation\u002Fspatial-statistics-and-pattern-analysis\u002Fgenerate-random-points-and-sampling-pyqgis",{"title":5,"description":1872},"spatial-data-processing-automation\u002Fspatial-statistics-and-pattern-analysis\u002Fgenerate-random-points-and-sampling-pyqgis\u002Findex","6XKBr2QW-uQ1z4JXXLPRkbEzpmJ0y0hjxnBBF7hxIJA",1790966264922]