Tutorial 7: Changing actor sets and missing data

Goal. Real panel data are rarely tidy. Some people join the group after the first survey, some leave before the last one, and some do not answer at one wave. This tutorial shows how to tell nwsaom about this, so that it neither invents changes nor throws the data away.

Data. The Glasgow friendship data of Tutorial 1. The Glasgow data are complete, so we construct the problems ourselves: we pretend that some pupils joined or left and that some answers are missing, and we say exactly how. The constructed examples show how the commands work; they are not findings about the school. The commands were run with nwsaom 1.1.1 and the core 3.10.0 in Stata 18, and checked in Stata 17. The complete script and log are in example.do and example.log.

Two different problems

It helps to keep two situations apart.

  • Composition change. An actor is not part of the group at some time: a pupil who has not yet arrived, or who has left. Such an actor cannot send or receive ties while away, and the model must not count their absence as friendships that were dropped.
  • Missing data. An actor is part of the group, but we do not know their ties or their behavior at that wave, for example because they were ill on the day of the survey. The model then fills in a plausible value for the simulation and leaves the unknown part out when it compares the simulated and observed networks.

Composition change with present()

Suppose pupils 1 to 4 joined the school after the first wave and pupils 5 to 7 left after the second. nwsaom wants one 0/1 variable per wave that says who is present, and wave data that are prepared in a particular way: an actor who has not yet arrived has no ties in the earlier waves, and an actor who has left keeps the ties they had at the last wave they were present. The following Mata code prepares such networks from the three Glasgow waves (it is in the script; nwtomata copies a network to a Mata matrix and nwset turns a Stata matrix back into a network):

. nwtomata glasgow1, mat(G1)
. nwtomata glasgow2, mat(G2)
. nwtomata glasgow3, mat(G3)
. mata:
: G1[1::4, .] = J(4, 50, 0)
: G1[., 1::4] = J(50, 4, 0)
: G3[5::7, .] = G2[5::7, .]
: G3[., 5::7] = G2[., 5::7]
: st_matrix("C1", G1)
: st_matrix("C2", G2)
: st_matrix("C3", G3)
: end
. nwset, mat(C1) directed name(comp1)
. nwset, mat(C2) directed name(comp2)
. nwset, mat(C3) directed name(comp3)

Now the presence variables, one per wave:

. generate byte pres1 = _n > 4
. generate byte pres2 = 1
. generate byte pres3 = !inrange(_n, 5, 7)

A quick check that they mark the right numbers of pupils:

. tabulate pres1

      pres1 |      Freq.     Percent        Cum.
------------+-----------------------------------
          0 |          4        8.00        8.00
          1 |         46       92.00      100.00
------------+-----------------------------------
      Total |         50      100.00

and the model, with present() listing the three variables in the order of the waves:

. nwsaom, waves(comp1 comp2 comp3) outdegree reciprocity transtrip present(pres1 pres2 pres3) seed(4)
----------------------------------------------------------------------------------------------------
SAOM (Method of Moments, unconditional), waves: comp1 comp2 comp3
Actors: 50                             Periods: 2
----------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------
comp1_to_c~3 | Coefficient  Std. err.      z    P>|z|     [95% conf. interval]
-------------+----------------------------------------------------------------
   outdegree |  -2.514709   .1123886   -22.38   0.000    -2.734987   -2.294432
 reciprocity |   2.383011   .1944882    12.25   0.000     2.001821    2.764201
   transtrip |   .5621572   .0847645     6.63   0.000     .3960219    .7282926
------------------------------------------------------------------------------
Rate parameters (estimated, one per inter-wave period):

             |   period1    period2 
-------------+---------------------
        rate |    8.2471     4.5747 
          se |    1.7841     0.7200 
Overall maximum convergence ratio: 0.169 (criterion: below 0.25; met)
Convergence t-ratios (criterion: all below 0.1 in absolute value): met

Three things to notice. First, the header says unconditional. With changing actor sets the estimation must be unconditional (the rates are estimated together with the other parameters, as described in Tutorial 12); nwsaom switches to it by itself. Second, the rates (8.25 and 4.57) differ from those of the complete data (6.50 and 5.26), because they now describe the actors who are present in a period; a pupil who is away is not given opportunities to change. Third, the coefficients are close to those of the complete data, as they should be, because we only hid a few pupils.

What happens if the model is not told?

For comparison, the same networks without present():

. nwsaom, waves(comp1 comp2 comp3) outdegree reciprocity transtrip seed(4)
----------------------------------------------------------------------------------------------------
SAOM (Method of Moments, conditional), waves: comp1 comp2 comp3
Actors: 50                             Periods: 2
----------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------
comp1_to_c~3 | Coefficient  Std. err.      z    P>|z|     [95% conf. interval]
-------------+----------------------------------------------------------------
   outdegree |   -2.62262   .1121841   -23.38   0.000    -2.842496   -2.402743
 reciprocity |   2.456261    .196206    12.52   0.000     2.071704    2.840818
   transtrip |   .6072784   .0879736     6.90   0.000     .4348533    .7797034
------------------------------------------------------------------------------
Rate parameters (estimated, one per inter-wave period):

             |   period1    period2 
-------------+---------------------
        rate |    7.5197     4.2130 
          se |    1.4456     0.6905 
Overall maximum convergence ratio: 0.066 (criterion: below 0.25; met)
Convergence t-ratios (criterion: all below 0.1 in absolute value): met

Here the estimates are also similar, because only seven of 50 pupils are involved. The difference is in what the model believes: the joiners appear to have created all their ties between waves one and two and the leavers to have kept theirs, and these are treated as ordinary choices of ordinary actors. With a larger share of joiners and leavers, or with a stronger structure in the network, such a model is wrong in a way that no output will tell you. Whenever the group changes, declare it.

model outdegree reciprocity transtrip rate 1 rate 2
complete data -2.70 2.47 0.63 6.50 5.26
joiners and leavers, present() -2.51 2.38 0.56 8.25 4.57
joiners and leavers, not declared -2.62 2.46 0.61 7.52 4.21

Joiners and leavers at any time: joinat() and leaveat()

present() assumes that actors come and go at the waves. In reality a pupil may arrive in March and leave in October. RSiena’s way to describe this, which nwsaom follows, puts time on a scale on which wave 1 is time 1 and wave 3 is time 3, and gives each actor the time they join and the time they leave. Pupils 1 to 4 now join at time 1.5, halfway between the first and second wave, and pupils 5 to 7 leave at time 2.5. Actors keep an empty row and column in the waves in which they are not there:

. generate double jn = 1
. replace jn = 1.5 if _n <= 4
. generate double lv = 3
. replace lv = 2.5 if inrange(_n, 5, 7)

jn is the joining time (1 for everybody who is there from the start) and lv the leaving time (3 for everybody who stays to the end):

. nwsaom, waves(timed1 timed2 timed3) outdegree reciprocity transtrip joinat(jn) leaveat(lv) seed(4)
----------------------------------------------------------------------------------------------------
SAOM (Method of Moments, unconditional), waves: timed1 timed2 timed3
Actors: 50                             Periods: 2
----------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------
timed1_to_~3 | Coefficient  Std. err.      z    P>|z|     [95% conf. interval]
-------------+----------------------------------------------------------------
   outdegree |  -2.613242   .1205242   -21.68   0.000    -2.849465   -2.377019
 reciprocity |   2.467177   .2110509    11.69   0.000     2.053525    2.880829
   transtrip |    .570416   .0827945     6.89   0.000     .4081418    .7326903
------------------------------------------------------------------------------
Rate parameters (estimated, one per inter-wave period):

             |   period1    period2 
-------------+---------------------
        rate |    8.1907     6.4872 
          se |    2.0645     1.0973 
Overall maximum convergence ratio: 0.140 (criterion: below 0.25; met)
Convergence t-ratios (criterion: all below 0.1 in absolute value): met

The pupils who join halfway through period one are active during the second half of it only, and the pupils who leave halfway through period two are active during its first half only; the model counts the time each pupil is present. The rates (8.19 and 6.49) are therefore not comparable with those of the present() model, in which a pupil is either present for a whole period or not at all. Use joinat() and leaveat() when you know the dates; use present() when you only know the waves.

Missing data

Missing ties: missnet()

Now the pupils are all present, but pupils 8 and 9 did not return the questionnaire in wave 2, so we do not know whom they named. missnet() takes one 0/1 matrix per wave; a 1 marks a dyad whose value is unknown. Here the whole outgoing rows of pupils 8 and 9 are missing in wave 2:

. mata:
: M2 = J(50, 50, 0)
: M2[8::9, .] = J(2, 50, 1)
: _diag(M2, 0)
: st_matrix("miss1", J(50, 50, 0))
: st_matrix("miss2", M2)
: st_matrix("miss3", J(50, 50, 0))
: end
. nwsaom, waves(glasgow1 glasgow2 glasgow3) outdegree reciprocity transtrip missnet(miss1 miss2 miss3) seed(4)
----------------------------------------------------------------------------------------------------
SAOM (Method of Moments, conditional), waves: glasgow1 glasgow2 glasgow3
Actors: 50                             Periods: 2
----------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------
glasgow1_t~3 | Coefficient  Std. err.      z    P>|z|     [95% conf. interval]
-------------+----------------------------------------------------------------
   outdegree |  -2.747335   .1397676   -19.66   0.000    -3.021274   -2.473395
 reciprocity |   2.544075   .2319734    10.97   0.000     2.089416    2.998735
   transtrip |   .6478841   .0805108     8.05   0.000     .4900857    .8056824
------------------------------------------------------------------------------
Rate parameters (estimated, one per inter-wave period):

             |   period1    period2 
-------------+---------------------
        rate |    6.1345     5.2180 
          se |    1.0683     0.8978 
Overall maximum convergence ratio: 0.072 (criterion: below 0.25; met)
Convergence t-ratios (criterion: all below 0.1 in absolute value): met

The model uses the last observed value to fill in the unknown dyads for the simulation and leaves the dyads out of the statistics that are compared with the data. The estimates (-2.75, 2.54 and 0.65) are again close to the full-data values, with somewhat larger standard errors, which is exactly what a loss of information should do. Note that a missing dyad is a Stata matrix, not a network.

Missing behavior: missbeh()

Missing behavior works the same way with one 0/1 variable per wave. Here pupils 8 and 9 have no alcohol value in wave 2 (mb2 is 1 for them):

. generate byte mb1 = 0
. generate byte mb2 = inlist(_n, 8, 9)
. generate byte mb3 = 0
. nwsaom, waves(glasgow1 glasgow2 glasgow3) outdegree reciprocity gwesp(.69) behsim behavior(alcohol1 alcohol2 alcohol3) linear quadratic avalt missbeh(mb1 mb2 mb3) seed(4)
----------------------------------------------------------------------------------------------------
SAOM co-evolution (Method of Moments), waves: glasgow1 glasgow2 glasgow3
Actors: 50                             Periods: 2
Behavior: alcohol1 alcohol2 alcohol3
----------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------
glasgow1_to~3 | Coefficient  Std. err.      z    P>|z|     [95% conf. interval]
--------------+----------------------------------------------------------------
    outdegree |  -2.922766   .1522532   -19.20   0.000    -3.221176   -2.624355
  reciprocity |   2.468063   .1988948    12.41   0.000     2.078236    2.857889
    gwesp_.69 |   1.604373   .1615976     9.93   0.000     1.287647    1.921098
       behsim |   1.426698    .615931     2.32   0.021     .2194952      2.6339
   beh_linear |   .4229424   .2706495     1.56   0.118    -.1075208    .9534057
beh_quadratic |  -.6064355   .4572518    -1.33   0.185    -1.502633    .2897615
    beh_avalt |   1.346736   1.122016     1.20   0.230    -.8523742    3.545846
-------------------------------------------------------------------------------
Rate parameters (estimated, one per inter-wave period):

             |   period1    period2 
-------------+---------------------
network_rate |    6.5868     5.1593 
          se |    1.2039     0.9752 
behavior_r~e |    1.1568     1.8091 
          se |    0.3251     0.4882 
Overall maximum convergence ratio: 0.110 (criterion: below 0.25; met)
Convergence t-ratios (criterion: all below 0.1 in absolute value): met

This is the co-evolution model of Tutorial 4, read in the same way (selection through behsim is 1.43, z = 2.3; influence through avalt is 1.35 with a large standard error); missbeh() needs behavior(). For avalt and avsim the treatment of missing values is an approximation, which is the more noticeable the more values are missing; the package help describes the limits.

Dyads that cannot change: structural zeros and ones

Sometimes some pairs of actors can never be tied (for example pupils in different classrooms), or are always tied. RSiena calls these structural zeros and structural ones. They go into the wave data as special values: 10 marks a dyad that is fixed at “no tie” and 11 a dyad that is fixed at “tie”. nwsaom recognizes the codes by itself, no option is needed, and they may differ from wave to wave. As an illustration we pretend that pupils 1 to 25 and pupils 26 to 50 sit in different classrooms, so that no tie can form between the two groups. This changes the Glasgow data, so the numbers are not findings about the school.

. nwtomata glasgow1, mat(S1)
. nwtomata glasgow2, mat(S2)
. nwtomata glasgow3, mat(S3)
. mata:
: S1[1::25, 26::50] = J(25, 25, 10)
: S1[26::50, 1::25] = J(25, 25, 10)
: (the same two lines for S2 and S3)
: st_matrix("SZ1", S1)
: st_matrix("SZ2", S2)
: st_matrix("SZ3", S3)
: end
. nwset, mat(SZ1) directed name(sz1)
. nwset, mat(SZ2) directed name(sz2)
. nwset, mat(SZ3) directed name(sz3)
. nwsaom, waves(sz1 sz2 sz3) outdegree reciprocity transtrip seed(1)
----------------------------------------------------------------------------------------------------
SAOM (Method of Moments, conditional), waves: sz1 sz2 sz3
Actors: 50                             Periods: 2
----------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------
  sz1_to_sz3 | Coefficient  Std. err.      z    P>|z|     [95% conf. interval]
-------------+----------------------------------------------------------------
   outdegree |  -2.998832   .2326355   -12.89   0.000    -3.454789   -2.542874
 reciprocity |   2.956383      .3586     8.24   0.000      2.25354    3.659226
   transtrip |    .669171   .1110585     6.03   0.000     .4515003    .8868417
------------------------------------------------------------------------------
Rate parameters (estimated, one per inter-wave period):

             |   period1    period2 
-------------+----------------------
        rate |    4.7306     3.0467 
          se |    0.8863     0.6340 
Overall maximum convergence ratio: 0.093 (criterion: below 0.25; met)
Convergence t-ratios (criterion: all below 0.1 in absolute value): met

There are 1,250 fixed dyads in each wave (25 x 25 in each direction). Fixed dyads cannot change, so ties can only form or be dropped between pupils in the same group, and the estimates describe tie formation inside the two groups. Use 10 for dyads that must stay empty and 11 for dyads that must stay tied. RSiena uses the same codes, and the older option structural() that takes a matrix still works but is deprecated.

Things to try

  • Let more pupils join or leave, and see how the rates change.
  • Combine present() with missnet(): both may be used together.
  • Make the data wrong on purpose (leave the ties of a leaver in the first wave but code the pupil as absent) and look at the result.
  • Add present() to the co-evolution model of Tutorial 4.

Where next

Tutorial 8 adds covariates that change over time and covariates defined for pairs of actors. The complete rules for composition change and missing data are in help nwsaom and help nwsaom_remarks.


Back to top

nwcommands is free software under the GNU General Public License, version 3, as are the model packages nwsaom, nwergm, nwdynam and nwrem. Citing and licenses · Get help · GitHub. Issues with the core go to the nwcommands repository; issues with a model package go to that package's own repository.

This site uses Just the Docs, a documentation theme for Jekyll.