Skip to content

Commit 7ee23cf

Browse files
authored
✨ Initial commit for branch "dev_denton" (ndi v0.1.6.9010) (#27)
* Added `denton()` function to compute the aspatial racial or ethnic Relative Clustering (*RCL*) based on [Massey & Denton (1988)](https://doi.org/10.1093/sf/67.2.281)
1 parent 5ea6a28 commit 7ee23cf

16 files changed

+1330
-427
lines changed

DESCRIPTION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: ndi
22
Title: Neighborhood Deprivation Indices
3-
Version: 0.1.6.9009
3+
Version: 0.1.6.9010
44
Date: 2024-08-30
55
Authors@R:
66
c(person(given = "Ian D.",

NAMESPACE

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export(atkinson)
55
export(bell)
66
export(bemanian_beyer)
77
export(bravo)
8+
export(denton)
89
export(duncan)
910
export(duncan_cuzzort)
1011
export(duncan_duncan)

NEWS.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ndi (development version)
22

3-
## ndi v0.1.6.9009
3+
## ndi v0.1.6.9010
44

55
### New Features
66

@@ -12,11 +12,12 @@
1212
* Added `theil()` function the aspatial racial or ethnic Entropy (*H*) based on Theil (1972; ISBN:978-0-444-10378-9) and [Theil & Finizza (1971)](https://doi.org/110.1080/0022250X.1971.9989795)
1313
* Added `white_blau()` function to compute an index of spatial proximity (*SP*) based on [White (1986)](https://doi.org/10.2307/3644339) and Blau (1977; ISBN-13:978-0-029-03660-0)
1414
* Thank you for the feature suggestions above, [Symielle Gaston](https://orcid.org/0000-0001-9495-1592)
15+
* Added `denton()` function to compute the aspatial racial or ethnic Relative Clustering (*RCL*) based on [Massey & Denton (1988)](https://doi.org/10.1093/sf/67.2.281)
1516
* Added `duncan_duncan()` function to compute the aspatial racial or ethnic Relative Centralization (*RCE*) based on [Duncan & Duncan (1955b)](https://doi.org/10.1086/221609) and [Massey & Denton (1988)](https://doi.org/10.1093/sf/67.2.281)
1617
* Added `massey()` function to compute the aspatial racial or ethnic Absolute Clustering (*ACL*) based on [Massey & Denton (1988)](https://doi.org/10.1093/sf/67.2.281)
1718

1819
#### New Function Capabilities
19-
* Added `geo_large = 'place'` for census-designated places, `geo_large = 'cbsa'` for core-based statistical areas, `geo_large = 'csa'` for combined statistical areas, and `geo_large = 'metro'` for metropolitan divisions as the larger geographical unit in `atkinson()`, `bell()`, `bemanian_beyer()`, `duncan()`, `duncan_cuzzort()`, `duncan_duncan()`, `hoover()`, `james_taeuber()`, `lieberson()`, `sudano()`, `theil()`, and `white()`, `white_blau()` functions.
20+
* Added `geo_large = 'place'` for census-designated places, `geo_large = 'cbsa'` for core-based statistical areas, `geo_large = 'csa'` for combined statistical areas, and `geo_large = 'metro'` for metropolitan divisions as the larger geographical unit in `atkinson()`, `bell()`, `bemanian_beyer()`, `denton()`, `duncan()`, `duncan_cuzzort()`, `duncan_duncan()`, `hoover()`, `james_taeuber()`, `lieberson()`, `sudano()`, `theil()`, and `white()`, `white_blau()` functions.
2021
* Added census block group computation for `anthopolos()` by specifying `geo == 'cbg'` or `geo == 'block group'`
2122
* Added `holder` argument to `atkinson()` function to toggle the computation with or without the Hölder mean. The function can now compute *A* without the Hölder mean. The default is `holder = FALSE`.
2223
* Added `crs` argument to `anthopolos()`, `bravo()`, and `white_blau()` functions to provide spatial projection of the distance-based metrics

R/denton.R

+453
Large diffs are not rendered by default.

R/globals.R

+2-1
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ globalVariables(
270270
'd',
271271
'crs',
272272
'RCE',
273-
'ACL'
273+
'ACL',
274+
'RCL'
274275
)
275276
)

R/ndi-package.R

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#'
2323
#' \code{\link{bemanian_beyer}} Computes the aspatial Local Exposure and Isolation (\emph{LEx/Is}) based on Bemanian & Beyer (2017) \doi{10.1158/1055-9965.EPI-16-0926}.
2424
#'
25+
#' \code{\link{denton}} Computes the aspatial Relative Clustering (\emph{RCL}) based on Massey & Denton (1988) \doi{10.1093/sf/67.2.281}.
26+
#'
2527
#' \code{\link{duncan}} Computes the aspatial Dissimilarity Index (\emph{D}) based on Duncan & Duncan (1955a) \doi{10.2307/2088328}.
2628
#'
2729
#' \code{\link{duncan_cuzzort}} Computes the aspatial Absolute Centralization (\emph{ACE}) based on Duncan, Cuzzort, & Duncan (1961; LC:60007089) and Massey & Denton (1988) \doi{10.1093/sf/67.2.281}.

R/utils.R

+67-30
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ ddd_fun <- function(x, omit_NAs) {
88
NA
99
} else {
1010
x_i <- xx$subgroup
11-
n_i <- sum(xx$subgroup, na.rm = TRUE)
11+
n_i <- sum(x_i, na.rm = TRUE)
1212
y_i <- xx$subgroup_ref
13-
m_i <- sum(xx$subgroup_ref, na.rm = TRUE)
13+
m_i <- sum(y_i, na.rm = TRUE)
1414
D <- 0.5 * sum(abs((x_i/n_i) - (y_i/m_i)), na.rm = TRUE)
1515
return(D)
1616
}
@@ -38,9 +38,9 @@ a_fun <- function(x, epsilon, omit_NAs, holder) {
3838
}
3939
} else {
4040
x_i <- xx$subgroup
41-
X <- sum(xx$subgroup, na.rm = TRUE)
41+
X <- sum(x_i, na.rm = TRUE)
4242
t_i <- xx$TotalPopE
43-
N <- sum(xx$TotalPopE, na.rm = TRUE)
43+
N <- sum(t_i, na.rm = TRUE)
4444
p_i <- x_i / t_i
4545
P <- X / N
4646
b <- epsilon
@@ -60,7 +60,7 @@ xpy_star_fun <- function(x, omit_NAs) {
6060
NA
6161
} else {
6262
x_i <- xx$subgroup
63-
X <- sum(xx$subgroup, na.rm = TRUE)
63+
X <- sum(x_i, na.rm = TRUE)
6464
y_i <- xx$subgroup_ixn
6565
t_i <- xx$TotalPopE
6666
xPy_star <- sum((x_i / X) * (y_i / t_i), na.rm = TRUE)
@@ -78,7 +78,7 @@ xpx_star_fun <- function(x, omit_NAs) {
7878
NA
7979
} else {
8080
x_i <- xx$subgroup
81-
X <- sum(xx$subgroup, na.rm = TRUE)
81+
X <- sum(x_i, na.rm = TRUE)
8282
t_i <- xx$TotalPopE
8383
xPx_star <- sum((x_i / X) * (x_i / t_i), na.rm = TRUE)
8484
return(xPx_star)
@@ -95,9 +95,9 @@ v_fun <- function(x, omit_NAs) {
9595
NA
9696
} else {
9797
x_i <- xx$subgroup
98-
X <- sum(xx$subgroup, na.rm = TRUE)
98+
X <- sum(x_i, na.rm = TRUE)
9999
t_i <- xx$TotalPopE
100-
N <- sum(xx$TotalPopE, na.rm = TRUE)
100+
N <- sum(t_i, na.rm = TRUE)
101101
xPx_star <- sum((x_i / X) * (x_i / t_i), na.rm = TRUE)
102102
P <- X / N
103103
V <- (xPx_star - P) / (1 - P)
@@ -117,8 +117,8 @@ lq_fun <- function(x, omit_NAs) {
117117
x_i <- xx$subgroup # x_im
118118
t_i <- xx$TotalPopE # X_i
119119
p_i <- x_i / t_i # p_im
120-
X <- sum(xx$subgroup, na.rm = TRUE) # X_m
121-
N <- sum(xx$TotalPopE, na.rm = TRUE) # X
120+
X <- sum(x_i, na.rm = TRUE) # X_m
121+
N <- sum(t_i, na.rm = TRUE) # X
122122
if (anyNA(p_i)) { p_i[is.na(p_i)] <- 0 }
123123
LQ <- p_i / (X / N) # (x_im/X_i)/(X_m/X)
124124
df <- data.frame(LQ = LQ, GEOID = xx$GEOID)
@@ -139,8 +139,14 @@ lexis_fun <- function(x, omit_NAs) {
139139
if (anyNA(p_im)) { p_im[is.na(p_im)] <- 0 }
140140
p_in <- xx$subgroup_ixn / xx$TotalPopE
141141
if (anyNA(p_in)) { p_in[is.na(p_in) ] <- 0 }
142-
P_m <- sum(xx$subgroup, na.rm = TRUE) / sum(xx$TotalPopE, na.rm = TRUE)
143-
P_n <- sum(xx$subgroup_ixn, na.rm = TRUE) / sum(xx$TotalPopE, na.rm = TRUE)
142+
x_i <- xx$subgroup
143+
X <- sum(x_i, na.rm = TRUE)
144+
y_i <- xx$subgroup_ixn
145+
Y <- sum(y_i, na.rm = TRUE)
146+
t_i <- xx$TotalPopE
147+
N <- sum(t_i, na.rm = TRUE)
148+
P_m <- X / N
149+
P_n <- Y / N
144150
LExIs <- car::logit(p_im * p_in) - car::logit(P_m * P_n)
145151
df <- data.frame(LExIs = LExIs, GEOID = xx$GEOID)
146152
return(df)
@@ -157,9 +163,9 @@ del_fun <- function(x, omit_NAs) {
157163
NA
158164
} else {
159165
x_i <- xx$subgroup
160-
X <- sum(xx$subgroup, na.rm = TRUE)
166+
X <- sum(x_i, na.rm = TRUE)
161167
a_i <- xx$ALAND
162-
A <- sum(xx$ALAND, na.rm = TRUE)
168+
A <- sum(a_i, na.rm = TRUE)
163169
DEL <- 0.5 * sum(abs((x_i / X) - (a_i / A)), na.rm = TRUE)
164170
return(DEL)
165171
}
@@ -181,12 +187,15 @@ sp_fun <- function(x, crs, omit_NAs) {
181187
units::set_units(value = km) %>%
182188
units::drop_units() %>%
183189
exp()
184-
X <- sum(xx$subgroup, na.rm = TRUE)
185-
Y <- sum(xx$subgroup_ref, na.rm = TRUE)
186-
N <- sum(xx$TotalPopE, na.rm = TRUE)
187-
P_xx <- sum((xx$subgroup * xx$subgroup * c_ij) / X^2, na.rm = TRUE)
188-
P_xy <- sum((xx$subgroup * xx$subgroup_ref * c_ij) / (X * Y), na.rm = TRUE)
189-
P_tt <- sum((xx$TotalPopE * xx$TotalPopE * c_ij) / N^2, na.rm = TRUE)
190+
x_i <- xx$subgroup
191+
X <- sum(x_i, na.rm = TRUE)
192+
y_i <- xx$subgroup_ref
193+
Y <- sum(y_i, na.rm = TRUE)
194+
t_i <- xx$TotalPopE
195+
N <- sum(t_i, na.rm = TRUE)
196+
P_xx <- sum((x_i * x_i * c_ij) / X^2, na.rm = TRUE)
197+
P_xy <- sum((x_i * y_i * c_ij) / (X * Y), na.rm = TRUE)
198+
P_tt <- sum((t_i * t_i * c_ij) / N^2, na.rm = TRUE)
190199
SP <- ((X * P_xx) + (Y * P_xy)) / (N * P_tt)
191200
return(SP)
192201
}
@@ -202,9 +211,9 @@ g_fun <- function(x, omit_NAs) {
202211
NA
203212
} else {
204213
x_i <- xx$subgroup
205-
X <- sum(xx$subgroup, na.rm = TRUE)
214+
X <- sum(x_i, na.rm = TRUE)
206215
t_i <- xx$TotalPopE
207-
N <- sum(xx$TotalPopE, na.rm = TRUE)
216+
N <- sum(t_i, na.rm = TRUE)
208217
p_i <- x_i / t_i
209218
P <- X / N
210219
titj <- apply(expand.grid(t_i, t_i), MARGIN = 1, FUN = prod)
@@ -225,9 +234,9 @@ djt_fun <- function(x, omit_NAs) {
225234
NA
226235
} else {
227236
x_i <- xx$subgroup
228-
X <- sum(xx$subgroup, na.rm = TRUE)
237+
X <- sum(x_i, na.rm = TRUE)
229238
t_i <- xx$TotalPopE
230-
N <- sum(xx$TotalPopE, na.rm = TRUE)
239+
N <- sum(t_i, na.rm = TRUE)
231240
p_i <- x_i / t_i
232241
P <- X / N
233242
D <- sum(t_i * abs(p_i - P), na.rm = TRUE) / (2 * N * P * (1 - P))
@@ -249,9 +258,9 @@ h_fun <- function(x, omit_NAs) {
249258
NA
250259
} else {
251260
x_i <- xx$subgroup
252-
X <- sum(xx$subgroup, na.rm = TRUE)
261+
X <- sum(x_i, na.rm = TRUE)
253262
t_i <- xx$TotalPopE
254-
N <- sum(xx$TotalPopE, na.rm = TRUE)
263+
N <- sum(t_i, na.rm = TRUE)
255264
p_i <- x_i / t_i
256265
p_i[is.infinite(p_i)] <- 0
257266
P <- X / N
@@ -291,8 +300,9 @@ ace_fun <- function(x, lgeom, crs, omit_NAs) {
291300
sf::st_drop_geometry()
292301
x_i <- xx$subgroup
293302
x_n <- sum(x_i, na.rm = TRUE)
294-
X_i <- cumsum(x_i / x_n)
295-
A_i <- cumsum(xx$ALAND / A$ALAND)
303+
X_i <- cumsum(x_i / x_n)
304+
a_i <- xx$ALAND
305+
A_i <- cumsum(a_i / A$ALAND)
296306
I_i <- matrix(c(seq(1, (length(x_i)-1), 1), seq(2, length(x_i), 1)), ncol = 2)
297307
Xi_1Ai <- sum(X_i[I_i[, 1]] * A_i[I_i[, 2]], na.rm = TRUE)
298308
XiA1_1 <- sum(X_i[I_i[, 2]] * A_i[I_i[, 1]], na.rm = TRUE)
@@ -351,12 +361,39 @@ acl_fun <- function(x, crs, omit_NAs) {
351361
units::drop_units() %>%
352362
exp()
353363
x_i <- xx$subgroup
354-
X <- sum(xx$subgroup, na.rm = TRUE)
355-
n <- length(xx$subgroup)
364+
X <- sum(x_i, na.rm = TRUE)
365+
n <- length(x_i)
356366
t_i <- xx$TotalPopE
357367
num <- (sum(x_i / X, na.rm = TRUE) * sum(c_ij * x_i, na.rm = TRUE)) - ((X / n^2) * sum(c_ij, na.rm = TRUE))
358368
denom <- (sum(x_i / X, na.rm = TRUE) * sum(c_ij * t_i, na.rm = TRUE)) - ((X / n^2) * sum(c_ij, na.rm = TRUE))
359369
ACL <- num / denom
360370
return(ACL)
361371
}
362372
}
373+
374+
# Internal function for Relative Clustering
375+
## From Denton & Massey (1988) https://doi.org/10.1093/sf/67.2.281
376+
## Returns NA value if only one smaller geography in a larger geography
377+
rcl_fun <- function(x, crs, omit_NAs) {
378+
xx <- x[ , c('subgroup', 'subgroup_ref', 'ALAND')]
379+
if (omit_NAs == TRUE) { xx <- xx[stats::complete.cases(sf::st_drop_geometry(xx)), ] }
380+
if (nrow(sf::st_drop_geometry(x)) < 2 || any(sf::st_drop_geometry(xx) < 0) || any(is.na(sf::st_drop_geometry(xx)))) {
381+
NA
382+
} else {
383+
xx <- xx %>% sf::st_transform(crs = crs)
384+
d_ij <- suppressWarnings(sf::st_distance(sf::st_centroid(xx), sf::st_centroid(xx)))
385+
diag(d_ij) <- sqrt(0.6 * xx$ALAND)
386+
c_ij <- -d_ij %>%
387+
units::set_units(value = km) %>%
388+
units::drop_units() %>%
389+
exp()
390+
x_i <- xx$subgroup
391+
X <- sum(x_i, na.rm = TRUE)
392+
y_i <- xx$subgroup_ref
393+
Y <- sum(y_i, na.rm = TRUE)
394+
P_xx <- sum((x_i * x_i * c_ij) / X^2, na.rm = TRUE)
395+
P_yy <- sum((y_i * y_i * c_ij) / Y^2, na.rm = TRUE)
396+
RCL <- (P_xx / P_yy) - 1
397+
return(RCL)
398+
}
399+
}

README.md

+63-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
[![DOI](https://zenodo.org/badge/521439746.svg)](https://zenodo.org/badge/latestdoi/521439746)
1313
<!-- badges: end -->
1414

15-
**Date repository last updated**: 2024-08-29
15+
**Date repository last updated**: 2024-08-30
1616

1717
### Overview
1818

@@ -63,6 +63,10 @@ To install the development version from GitHub:
6363
<td>Compute the spatial Educational Isolation Index (<i>EI</i>) based on <a href='https://doi.org/10.3390/ijerph18179384'>Bravo et al. (2021)</a></td>
6464
</tr>
6565
<tr>
66+
<td><a href='/R/denton.R'><code>denton</code></a></td>
67+
<td>Compute the aspatial racial or ethnic Relative Clustering (<i>RCL</i>) based on <a href='https://doi.org/10.1093/sf/67.2.281'>Massey & Denton (1988)</a></td>
68+
</tr>
69+
<tr>
6670
<td><a href='/R/duncan.R'><code>duncan</code></a></td>
6771
<td>Compute the aspatial racial or ethnic Dissimilarity Index (<i>D</i>) based on <a href='https://doi.org/10.2307/2088328'>Duncan & Duncan (1955a)</a></td>
6872
</tr>
@@ -286,6 +290,7 @@ ggplot() +
286290
subtitle = 'Washington, D.C. tracts as the referent'
287291
)
288292
```
293+
289294
![](man/figures/messer1.png)
290295
![](man/figures/messer2.png)
291296

@@ -323,7 +328,7 @@ powell_wiley_2020_DC$missing
323328
# Obtain the 2020 census tracts from the 'tigris' package
324329
tract_2020_DC <- tracts(state = 'DC', year = 2020, cb = TRUE)
325330

326-
# Join the NDI (powell_wiley) values to the census tract geometry
331+
# Join the NDI (Powell-Wiley) values to the census tract geometry
327332
DC_2020_powell_wiley <- tract_2020_DC %>%
328333
left_join(powell_wiley_2020_DC$ndi, by = 'GEOID')
329334
DC_2020_powell_wiley <- DC_2020_powell_wiley %>%
@@ -460,6 +465,8 @@ cor(NDI_2020_DC$NDI.messer, NDI_2020_DC$NDI.powell_wiley, use = 'complete.obs')
460465
table(NDI_2020_DC$NDIQuart, NDI_2020_DC$NDIQuint)
461466
```
462467

468+
#### Additional indices of racial or ethnic residential segregation or socioeconomic disparity
469+
463470
``` r
464471
# ---------------------------------------------------- #
465472
# Compute spatial Racial Isoliation Index (Anthopolos) #
@@ -726,6 +733,58 @@ ggplot() +
726733

727734
![](man/figures/ei.png)
728735

736+
```r
737+
# ------------------------------------------------------ #
738+
# Compute aspatial Relative Clustering (Massey & Denton) #
739+
# ------------------------------------------------------ #
740+
741+
# Relative Clustering based on Massey & Denton (1988)
742+
## Selected subgroup: Not Hispanic or Latino, Black or African American alone
743+
## Selected subgroup reference: Not Hispanic or Latino, white alone
744+
## Selected large geography: census tract
745+
## Selected small geography: census block group
746+
RCL_2020_DC <- denton(
747+
geo_large = 'tract',
748+
geo_small = 'cbg',
749+
state = 'DC',
750+
year = 2020,
751+
subgroup = 'NHoLB',
752+
subgroup_ref = 'NHoLW'
753+
)
754+
755+
# Obtain the 2020 census tracts from the 'tigris' package
756+
tract_2020_DC <- tracts(state = 'DC', year = 2020, cb = TRUE)
757+
758+
# Join the RCL (Massey & Denton) values to the census tract geometry
759+
RCL_2020_DC <- tract_2020_DC %>%
760+
left_join(RCL_2020_DC$rcl, by = 'GEOID')
761+
762+
ggplot() +
763+
geom_sf(
764+
data = RCL_2020_DC,
765+
aes(fill = RCL),
766+
color = 'white'
767+
) +
768+
theme_bw() +
769+
scale_fill_gradient2(
770+
low = '#998ec3',
771+
mid = '#f7f7f7',
772+
high = '#f1a340',
773+
midpoint = 0
774+
) +
775+
labs(
776+
fill = 'Index (Continuous)',
777+
caption = 'Source: U.S. Census ACS 2016-2020 estimates'
778+
) +
779+
ggtitle(
780+
'Relative Clustering (Massey & Denton)\n
781+
Washington, D.C. census block groups to tracts',
782+
subtitle = 'Black non-Hispanic vs. white non-Hispanic'
783+
)
784+
```
785+
786+
![](man/figures/rcl.png)
787+
729788
```r
730789
# ----------------------------------------------------------------------- #
731790
# Compute aspatial racial or ethnic Dissimilarity Index (Duncan & Duncan) #
@@ -846,7 +905,7 @@ RCE_2020_DC <- duncan_duncan(
846905
# Obtain the 2020 census tracts from the 'tigris' package
847906
tract_2020_DC <- tracts(state = 'DC', year = 2020, cb = TRUE)
848907

849-
# Join the ACE (Duncan & Cuzzort) values to the census tract geometry
908+
# Join the ACE (Duncan & Duncan) values to the census tract geometry
850909
RCE_2020_DC <- tract_2020_DC %>%
851910
left_join(RCE_2020_DC$rce, by = 'GEOID')
852911

@@ -1278,7 +1337,7 @@ ACL_2020_DC <- massey(
12781337
# Obtain the 2020 census tracts from the 'tigris' package
12791338
tract_2020_DC <- tracts(state = 'DC', year = 2020, cb = TRUE)
12801339

1281-
# Join the ACL (Duncan & Cuzzort) values to the census tract geometry
1340+
# Join the ACL (Massey & Denton) values to the census tract geometry
12821341
ACL_2020_DC <- tract_2020_DC %>%
12831342
left_join(ACL_2020_DC$acl, by = 'GEOID')
12841343

0 commit comments

Comments
 (0)