{"id":16572631,"url":"https://github.com/knapply/constraint","last_synced_at":"2026-03-06T08:32:03.484Z","repository":{"id":105873185,"uuid":"272032493","full_name":"knapply/constraint","owner":"knapply","description":null,"archived":false,"fork":false,"pushed_at":"2020-06-14T21:34:54.000Z","size":381,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-05T14:28:42.231Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/knapply.png","metadata":{"files":{"readme":"README.Rmd","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-06-13T15:12:47.000Z","updated_at":"2020-06-14T21:34:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"4cb558c9-6645-4477-9406-de651afb5af5","html_url":"https://github.com/knapply/constraint","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/knapply/constraint","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knapply%2Fconstraint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knapply%2Fconstraint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knapply%2Fconstraint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knapply%2Fconstraint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/knapply","download_url":"https://codeload.github.com/knapply/constraint/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knapply%2Fconstraint/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30167962,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T07:56:45.623Z","status":"ssl_error","status_checked_at":"2026-03-06T07:55:55.621Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-11T21:28:08.756Z","updated_at":"2026-03-06T08:32:03.467Z","avatar_url":"https://github.com/knapply.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"---\ntitle: \"Constraint\"\nauthor: \"Brendan Knapp\"\noutput: \n  github_document:\n    toc: true\neditor_options: \n  chunk_output_type: console\nalways_allow_html: true\n---\n\n```{r setup, include=FALSE}\nknitr::opts_chunk$set(echo = TRUE, fig.align = \"center\",\n                      comment = \"#\u003e\")\noptions(width = 200)\n```\n\n```{r}\nlibrary(igraph, warn.conflicts = FALSE)\n```\n\n```{r}\nstrike_net \u003c- \"*Vertices      24\\r\\n     1 \\\"Xavier\\\"    0.8220    0.7997\\r\\n     2 \\\"Utrecht\\\"    0.7189    0.5679\\r\\n     3 \\\"Frank\\\"    0.1500    0.8500\\r\\n     4 \\\"Domingo\\\"    0.3480    0.1500\\r\\n     5 \\\"Norm\\\"    0.5938    0.5446\\r\\n     6 \\\"Hal\\\"    0.3316    0.6461\\r\\n     7 \\\"Russ\\\"    0.8359    0.4228\\r\\n     8 \\\"Karl\\\"    0.4469    0.8339\\r\\n     9 \\\"Bob\\\"    0.4608    0.5138\\r\\n     10 \\\"Quint\\\"    0.8500    0.4964\\r\\n     11 \\\"Wendle\\\"    0.8001    0.8384\\r\\n     12 \\\"Ozzie\\\"    0.5793    0.7488\\r\\n     13 \\\"Ike\\\"    0.3135    0.5410\\r\\n     14 \\\"Ted\\\"    0.8127    0.2755\\r\\n     15 \\\"Sam\\\"    0.7070    0.6662\\r\\n     16 \\\"Vern\\\"    0.6746    0.3554\\r\\n     17 \\\"Gill\\\"    0.2525    0.7417\\r\\n     18 \\\"Lanny\\\"    0.4151    0.6905\\r\\n     19 \\\"Mike\\\"    0.3520    0.4907\\r\\n     20 \\\"Carlos\\\"    0.2922    0.1971\\r\\n     21 \\\"Alejandro\\\"    0.3967    0.3307\\r\\n     22 \\\"Paul\\\"    0.6923    0.4682\\r\\n     23 \\\"Eduardo\\\"    0.3200    0.1712\\r\\n     24 \\\"John\\\"    0.3770    0.6861\\r\\n*Arcs\\r\\n      1     11    1.0000\\r\\n      1     15    1.0000\\r\\n      2      5    1.0000\\r\\n      2      7    1.0000\\r\\n      2     10    1.0000\\r\\n      2     15    1.0000\\r\\n      3     17    1.0000\\r\\n      4     20    1.0000\\r\\n      4     21    1.0000\\r\\n      4     23    1.0000\\r\\n      5      2    1.0000\\r\\n      5      9    1.0000\\r\\n      5     12    1.0000\\r\\n      5     15    1.0000\\r\\n      5     16    1.0000\\r\\n      5     22    1.0000\\r\\n      6      9    1.0000\\r\\n      6     17    1.0000\\r\\n      6     24    1.0000\\r\\n      7      2    1.0000\\r\\n      7     10    1.0000\\r\\n      7     14    1.0000\\r\\n      8     12    1.0000\\r\\n      8     18    1.0000\\r\\n      8     24    1.0000\\r\\n      9      5    1.0000\\r\\n      9      6    1.0000\\r\\n      9     13    1.0000\\r\\n      9     18    1.0000\\r\\n      9     19    1.0000\\r\\n      9     21    1.0000\\r\\n      9     24    1.0000\\r\\n     10      2    1.0000\\r\\n     10      7    1.0000\\r\\n     10     22    1.0000\\r\\n     11      1    1.0000\\r\\n     11     15    1.0000\\r\\n     12      5    1.0000\\r\\n     12      8    1.0000\\r\\n     13      9    1.0000\\r\\n     13     17    1.0000\\r\\n     13     19    1.0000\\r\\n     14      7    1.0000\\r\\n     14     16    1.0000\\r\\n     15      1    1.0000\\r\\n     15      2    1.0000\\r\\n     15      5    1.0000\\r\\n     15     11    1.0000\\r\\n     16      5    1.0000\\r\\n     16     14    1.0000\\r\\n     17      3    1.0000\\r\\n     17      6    1.0000\\r\\n     17     13    1.0000\\r\\n     17     24    1.0000\\r\\n     18      8    1.0000\\r\\n     18      9    1.0000\\r\\n     18     24    1.0000\\r\\n     19      9    1.0000\\r\\n     19     13    1.0000\\r\\n     20      4    1.0000\\r\\n     20     21    1.0000\\r\\n     20     23    1.0000\\r\\n     21      4    1.0000\\r\\n     21      9    1.0000\\r\\n     21     20    1.0000\\r\\n     21     23    1.0000\\r\\n     22      5    1.0000\\r\\n     22     10    1.0000\\r\\n     23      4    1.0000\\r\\n     23     20    1.0000\\r\\n     23     21    1.0000\\r\\n     24      6    1.0000\\r\\n     24      8    1.0000\\r\\n     24      9    1.0000\\r\\n     24     17    1.0000\\r\\n     24     18    1.0000\\r\\n\"\ntemp_file \u003c- tempfile(fileext = \".net\")\nreadr::write_file(strike_net, temp_file)\n\nstrike_g \u003c- as.undirected(\n  read_graph(temp_file, format = \"pajek\")\n)\n\nplot_igraph \u003c- function(g, ...) {\n  plot(g, asp = 0.5, \n       vertex.size = 4, vertex.label.dist = 1.5, ...)\n}\n\nplot_igraph(strike_g, main = \"Strike\")\n```\n\n\n# Ego Model\n\n## UCINET Results\n\n```\n                   1\n             Constra\n                 int\n             -------\n 1    Xavier   1.125\n 2   Utrecht   0.563\n 3     Frank       1 ???? missing in ego.txt  \n 4   Domingo   0.926\n 5      Norm   0.236\n 6       Hal   0.840\n 7      Russ   0.611\n 8      Karl   0.611\n 9       Bob   0.287\n10     Quint   0.611\n11    Wendle   1.125\n12     Ozzie   0.500\n13       Ike   0.611\n14       Ted   0.500\n15       Sam   0.563\n16      Vern   0.500\n17      Gill   0.406\n18     Lanny   0.840\n19      Mike   1.125\n20    Carlos   0.926\n21 Alejandro   0.583\n22      Paul   0.500\n23   Eduardo   0.926\n24      John   0.522\n```\n\n```{r}\nucinet_ego_results \u003c- c(\n  1.125, 0.563, 1, 0.926, 0.236, 0.840, 0.611, 0.611, 0.287, 0.611, 1.125, \n  0.500, 0.611, 0.500, 0.563, 0.500, 0.406, 0.840, 1.125, 0.926, 0.583, 0.500, 0.926, 0.522\n)\n```\n\n## `{igraph}` Replication\n\nExtract each vertex's ego (order = 1) network and run `igraph::constraint()` on them individually.\n\n```{r}\nego_constraint \u003c- function(g, .order = 1L, .round = 3L, .nm = FALSE) {\n  if (!is_named(g)) {\n    vertex_attr(g, \"name\") \u003c- sprintf(\"n%d\", seq_len(vcount(g)))\n  }\n  \n  ego_nets \u003c- make_ego_graph(g, order = .order) # get all ego networks\n  ego_names \u003c- vertex_attr(g, \"name\") # grab the egos' names\n  \n  # walk through each ego network and measure constraint of the ego vertex\n  out \u003c- mapply(function(.ego_net, .ego_name) { \n    constraint(.ego_net, nodes = .ego_name)\n  }, ego_nets, ego_names)\n  \n  out \u003c- round(out, .round)\n  if (.nm) out else unname(out)\n}\n```\n\n```{r}\nego_constraint(strike_g, .nm = TRUE)\n```\n\n## Confirmation\n\n```{r}\ndata.frame(\n  name = vertex_attr(strike_g, \"name\"), \n  `UCINET \"Ego\"` = round(ucinet_ego_results, 3),\n  `ego_constraint()` = ego_constraint(strike_g),\n  check.names = FALSE\n)\nstopifnot(\n  identical(\n    round(ucinet_ego_results, 2), \n    ego_constraint(strike_g, .round = 2)\n  )\n)\n```\n\n\n# \"Whole Network\"\n\nIdentical to `igraph::constraint()`.\n\n## UCINET Results\n\n```\n                  3\n              Const\n              raint\n              -----\n 1    Xavier  0.953\n 2   Utrecht  0.405\n 3     Frank      1\n 4   Domingo  0.866\n 5      Norm  0.198\n 6       Hal  0.536\n 7      Russ  0.482\n 8      Karl  0.469\n 9       Bob  0.238\n10     Quint  0.482\n11    Wendle  0.953\n12     Ozzie  0.500\n13       Ike  0.506\n14       Ted  0.500\n15       Sam  0.464\n16      Vern  0.500\n17      Gill  0.326\n18     Lanny  0.562\n19      Mike  0.771\n20    Carlos  0.866\n21 Alejandro  0.583\n22      Paul  0.500\n23   Eduardo  0.866\n24      John  0.418\n```\n\n```{r}\nucinet_whole_results \u003c- c( \n  0.953, 0.405, 1, 0.866, 0.198, 0.536, 0.482, 0.469, 0.238, 0.482, 0.953,\n  0.500, 0.506, 0.500, 0.464, 0.500, 0.326, 0.562, 0.771, 0.866, 0.583, \n  0.500, 0.866, 0.418\n)\n```\n\n## `{igraph}` Replication\n\n```{r}\nconstraint(strike_g)\n```\n\n## Confirmation\n\n```{r}\nstopifnot(\n  identical(\n    ucinet_whole_results,\n    constraint(strike_g) %\u003e% round(3L) %\u003e% unname()\n  )\n)\n```\n\n```{r}\ndata.frame(\n  name = vertex_attr(strike_g, \"name\"),\n  `UCINET \"Whole Network\"` = ucinet_whole_results,\n  `igraph::constraint()` = round(constraint(strike_g), 3L),\n  check.names = FALSE,\n  row.names = NULL\n)\n```\n\n## \"Whole Network\"?\n\nWhat UCINET is calling \"Whole Network\" is misleading.\n\n```{r}\nego_g \u003c- make_ego_graph(strike_g, nodes = \"Xavier\")[[1L]]\nplot_igraph(ego_g, main = \"Xavier's Ego\")\n```\n\n\n```{r}\nextended_ego_g \u003c- make_ego_graph(strike_g, order = 2L, nodes = \"Xavier\")[[1L]]\nplot_igraph(extended_ego_g, main = \"Xavier's Extended Ego\")\n```\n\n### Extended Ego\n\nHowever UCINET calulates it, \"Whole Network\" and `igraph::constraint()` measure the \"extended\" ego networks (order = 2).\n\n\n```{r}\nextended_ego_constraint \u003c- function(g, ...) {\n  ego_constraint(g, .order = 2L, ...)\n}\n\nextended_ego_constraint(strike_g)\n```\n\n#### Confirmation\n\n```{r}\nstopifnot(\n  identical(\n    constraint(strike_g) %\u003e% round(3) %\u003e% unname(),\n    extended_ego_constraint(strike_g)\n  )\n)\nstopifnot(\n  identical(\n    extended_ego_constraint(strike_g), \n    ucinet_whole_results\n  )\n)\n```\n\n\"Whole Network\" may describe UCINET's other \"Whole Network\" Structural Holes metrics, but not constraint.\n\n```{r}\ndata.frame(\n  `igraph::constraint()` = round(constraint(strike_g), 3L),\n  `extended_ego_constraint()` = extended_ego_constraint(strike_g),\n  `UCINET \"Whole Network\"` = ucinet_whole_results,\n  check.names = FALSE,\n  row.names = NULL\n)\n```\n\n\n\n\n# Maximum Constraint\n\nEither interpretation of Burt's Constraint can yield values \u003e 1.\n\n```{r}\nel \u003c- matrix(\n  c(1, 2, \n    1, 3, \n    2, 3), ncol = 2L, byrow = TRUE)\n\ng \u003c- graph_from_edgelist(el, directed = FALSE)\nvertex_attr(g, \"name\") \u003c- letters[seq_len(vcount(g))]\nplot(g)\n```\n\n```{r}\ndata.frame(\n    name = vertex_attr(g, \"name\"),\n    Ego = ego_constraint(g),\n    `\"Whole Network\"/Extended Ego` = constraint(g),\n    check.names = FALSE,\n    row.names = NULL\n)\n```\n\n\n```{r}\nel \u003c- matrix(\n  c(1, 2, \n    1, 3, \n    2, 3,\n    3, 4), ncol = 2L, byrow = TRUE)\n\ng \u003c- graph_from_edgelist(el, directed = FALSE) \nvertex_attr(g, \"name\") \u003c- letters[seq_len(vcount(g))]\nplot(g)\n```\n\n```{r}\ndata.frame(\n    name = vertex_attr(g, \"name\"),\n    Ego = ego_constraint(g),\n    `\"Whole Network\"/Extended Ego` = constraint(g),\n    check.names = FALSE,\n    row.names = NULL\n)\n```\n\n```{r}\nel \u003c- matrix(\n  c(1, 2, \n    1, 3, \n    2, 3,\n    3, 4,\n    4, 5), ncol = 2L, byrow = TRUE)\n\ng \u003c- graph_from_edgelist(el, directed = FALSE) \nvertex_attr(g, \"name\") \u003c- letters[seq_len(vcount(g))]\nplot(g)\n```\n\n```{r}\ndata.frame(\n    name = vertex_attr(g, \"name\"),\n    Ego = ego_constraint(g),\n    `\"Whole Network\"/Extended Ego` = constraint(g),\n    check.names = FALSE,\n    row.names = NULL\n)\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknapply%2Fconstraint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fknapply%2Fconstraint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknapply%2Fconstraint/lists"}