Once a dataset is cleaned and ready for statistical analysis, the
first step is typically to summarize it. The
univariate_table()
function makes it easy to create a
custom descriptive analysis while consistently producing clean,
presentation-ready output. It is built to integrate directly into your
analysis work flow (e.g. R markdown) but can also be called from the
console and be rendered in a number of formats.
## Loading required package: cheese
heart_disease %>%
univariate_table()
| Variable | Level | Summary |
|---|---|---|
| Age | 56 (48, 61) | |
| Sex | Female | 97 (32.01%) |
| Sex | Male | 206 (67.99%) |
| ChestPain | Typical angina | 23 (7.59%) |
| ChestPain | Atypical angina | 50 (16.5%) |
| ChestPain | Non-anginal pain | 86 (28.38%) |
| ChestPain | Asymptomatic | 144 (47.52%) |
| BP | 130 (120, 140) | |
| Cholesterol | 241 (211, 275) | |
| MaximumHR | 153 (133.5, 166) | |
| ExerciseInducedAngina | No | 204 (67.33%) |
| ExerciseInducedAngina | Yes | 99 (32.67%) |
| HeartDisease | No | 164 (54.13%) |
| HeartDisease | Yes | 139 (45.87%) |
By default, an HTML table is produced containing descriptive statistics for columns in the dataset.
In the table above, the summary statistics are presented within the
cells in a particular format for different types of data. You can use
the _summary
arguments to customize not only the appearance
that the results are presented with, but the values that go into the
results themselves.
Suppose instead of the "median (q1, q3)"
being displayed
for numeric data, you want the "mean [sd] / median"
, in
that exact format:
heart_disease %>%
univariate_table(
numeric_summary =
c(
Summary = "mean [sd] / median"
)
)
| Variable | Level | Summary |
|---|---|---|
| Age | 54.44 [9.04] / 56 | |
| Sex | Female | 97 (32.01%) |
| Sex | Male | 206 (67.99%) |
| ChestPain | Typical angina | 23 (7.59%) |
| ChestPain | Atypical angina | 50 (16.5%) |
| ChestPain | Non-anginal pain | 86 (28.38%) |
| ChestPain | Asymptomatic | 144 (47.52%) |
| BP | 131.69 [17.6] / 130 | |
| Cholesterol | 246.69 [51.78] / 241 | |
| MaximumHR | 149.61 [22.88] / 153 | |
| ExerciseInducedAngina | No | 204 (67.33%) |
| ExerciseInducedAngina | Yes | 99 (32.67%) |
| HeartDisease | No | 164 (54.13%) |
| HeartDisease | Yes | 139 (45.87%) |
The name Summary
was used to ensure that the result for
the numeric data binded in the same column as the result for the other
data types. If you chose to name it something else, you’d get a new
column with those summaries:
heart_disease %>%
univariate_table(
numeric_summary =
c(
NewSummary = "mean [sd] / median"
)
)
| Variable | Level | NewSummary | Summary |
|---|---|---|---|
| Age | 54.44 [9.04] / 56 | ||
| Sex | Female | 97 (32.01%) | |
| Sex | Male | 206 (67.99%) | |
| ChestPain | Typical angina | 23 (7.59%) | |
| ChestPain | Atypical angina | 50 (16.5%) | |
| ChestPain | Non-anginal pain | 86 (28.38%) | |
| ChestPain | Asymptomatic | 144 (47.52%) | |
| BP | 131.69 [17.6] / 130 | ||
| Cholesterol | 246.69 [51.78] / 241 | ||
| MaximumHR | 149.61 [22.88] / 153 | ||
| ExerciseInducedAngina | No | 204 (67.33%) | |
| ExerciseInducedAngina | Yes | 99 (32.67%) | |
| HeartDisease | No | 164 (54.13%) | |
| HeartDisease | Yes | 139 (45.87%) |
You can add as many summary columns as you want separately for each type of data:
heart_disease %>%
univariate_table(
numeric_summary =
c(
`Numeric only` = "mean [sd] / median",
Summary = "median (q1, q3)"
),
categorical_summary =
c(
Summary = "count",
`Categorical only` = "percent = 100 * proportion"
)
)
| Variable | Level | Numeric only | Summary | Categorical only |
|---|---|---|---|---|
| Age | 54.44 [9.04] / 56 | 56 (48, 61) | ||
| Sex | Female | 97 | 32.01 = 100 * 0.32 | |
| Sex | Male | 206 | 67.99 = 100 * 0.68 | |
| ChestPain | Typical angina | 23 | 7.59 = 100 * 0.08 | |
| ChestPain | Atypical angina | 50 | 16.5 = 100 * 0.17 | |
| ChestPain | Non-anginal pain | 86 | 28.38 = 100 * 0.28 | |
| ChestPain | Asymptomatic | 144 | 47.52 = 100 * 0.48 | |
| BP | 131.69 [17.6] / 130 | 130 (120, 140) | ||
| Cholesterol | 246.69 [51.78] / 241 | 241 (211, 275) | ||
| MaximumHR | 149.61 [22.88] / 153 | 153 (133.5, 166) | ||
| ExerciseInducedAngina | No | 204 | 67.33 = 100 * 0.67 | |
| ExerciseInducedAngina | Yes | 99 | 32.67 = 100 * 0.33 | |
| HeartDisease | No | 164 | 54.13 = 100 * 0.54 | |
| HeartDisease | Yes | 139 | 45.87 = 100 * 0.46 |
A more visually-appealing case for adding multiple summaries is probably when all the data is the same type:
heart_disease %>%
univariate_table(
categorical_types = NULL, #Easily disable categorical data from being summarized
numeric_summary =
c(
`Median (Q1, Q3)` = "median (q1, q3)",
`Min-Max` = "min - max",
`Mean (SD)` = "mean (sd)"
)
)
| Variable | Median (Q1, Q3) | Min-Max | Mean (SD) |
|---|---|---|---|
| Age | 56 (48, 61) | 29 - 77 | 54.44 (9.04) |
| BP | 130 (120, 140) | 94 - 200 | 131.69 (17.6) |
| Cholesterol | 241 (211, 275) | 126 - 564 | 246.69 (51.78) |
| MaximumHR | 153 (133.5, 166) | 71 - 202 | 149.61 (22.88) |
Or when adding a summary that applies to all columns:
heart_disease %>%
univariate_table(
all_summary =
c(
`# obs. non-missing` = "available of length"
)
)
| Variable | Level | Summary | # obs. non-missing |
|---|---|---|---|
| Age | 56 (48, 61) | 303 of 303 | |
| Sex | 303 of 303 | ||
| Sex | Female | 97 (32.01%) | |
| Sex | Male | 206 (67.99%) | |
| ChestPain | 303 of 303 | ||
| ChestPain | Typical angina | 23 (7.59%) | |
| ChestPain | Atypical angina | 50 (16.5%) | |
| ChestPain | Non-anginal pain | 86 (28.38%) | |
| ChestPain | Asymptomatic | 144 (47.52%) | |
| BP | 130 (120, 140) | 303 of 303 | |
| Cholesterol | 241 (211, 275) | 303 of 303 | |
| BloodSugar | 303 of 303 | ||
| MaximumHR | 153 (133.5, 166) | 303 of 303 | |
| ExerciseInducedAngina | 303 of 303 | ||
| ExerciseInducedAngina | No | 204 (67.33%) | |
| ExerciseInducedAngina | Yes | 99 (32.67%) | |
| HeartDisease | 303 of 303 | ||
| HeartDisease | No | 164 (54.13%) | |
| HeartDisease | Yes | 139 (45.87%) |
These add an extra row for categorical variables. You may have also
noticed that the BloodSugar
column didn’t show up in the
table until the all_summary
argument was used–this is
because it is not classified as numeric or categorical data, and thus
not evaluated by default. See the “Backend functionality” section to
learn more.
The strata
argument takes a formula()
that
can be used to stratify the analysis by any number of variables. Columns
on the left side will appear down the rows, and columns on the right
side will spread across the columns. You can use +
on
either side to specify more than one column. Let’s start by stratifying
sex across the columns:
heart_disease %>%
univariate_table(
strata = ~ Sex
)
| Variable | Level | Female | Male |
|---|---|---|---|
| Age | 57 (50, 63) | 54.5 (47, 59.75) | |
| ChestPain | Typical angina | 4 (4.12%) | 19 (9.22%) |
| ChestPain | Atypical angina | 18 (18.56%) | 32 (15.53%) |
| ChestPain | Non-anginal pain | 35 (36.08%) | 51 (24.76%) |
| ChestPain | Asymptomatic | 40 (41.24%) | 104 (50.49%) |
| BP | 132 (120, 140) | 130 (120, 140) | |
| Cholesterol | 254 (215, 302) | 235 (208.75, 268.5) | |
| MaximumHR | 157 (142, 165) | 150.5 (132, 167.5) | |
| ExerciseInducedAngina | No | 75 (77.32%) | 129 (62.62%) |
| ExerciseInducedAngina | Yes | 22 (22.68%) | 77 (37.38%) |
| HeartDisease | No | 72 (74.23%) | 92 (44.66%) |
| HeartDisease | Yes | 25 (25.77%) | 114 (55.34%) |
You can do the same thing down the rows:
heart_disease %>%
univariate_table(
strata = Sex ~ 1
)
| Sex | Variable | Level | Summary |
|---|---|---|---|
| Female | Age | 57 (50, 63) | |
| Female | ChestPain | Typical angina | 4 (4.12%) |
| Female | ChestPain | Atypical angina | 18 (18.56%) |
| Female | ChestPain | Non-anginal pain | 35 (36.08%) |
| Female | ChestPain | Asymptomatic | 40 (41.24%) |
| Female | BP | 132 (120, 140) | |
| Female | Cholesterol | 254 (215, 302) | |
| Female | MaximumHR | 157 (142, 165) | |
| Female | ExerciseInducedAngina | No | 75 (77.32%) |
| Female | ExerciseInducedAngina | Yes | 22 (22.68%) |
| Female | HeartDisease | No | 72 (74.23%) |
| Female | HeartDisease | Yes | 25 (25.77%) |
| Male | Age | 54.5 (47, 59.75) | |
| Male | ChestPain | Typical angina | 19 (9.22%) |
| Male | ChestPain | Atypical angina | 32 (15.53%) |
| Male | ChestPain | Non-anginal pain | 51 (24.76%) |
| Male | ChestPain | Asymptomatic | 104 (50.49%) |
| Male | BP | 130 (120, 140) | |
| Male | Cholesterol | 235 (208.75, 268.5) | |
| Male | MaximumHR | 150.5 (132, 167.5) | |
| Male | ExerciseInducedAngina | No | 129 (62.62%) |
| Male | ExerciseInducedAngina | Yes | 77 (37.38%) |
| Male | HeartDisease | No | 92 (44.66%) |
| Male | HeartDisease | Yes | 114 (55.34%) |
Or even both:
heart_disease %>%
univariate_table(
strata = Sex ~ HeartDisease
)
| Sex | Variable | Level | No | Yes |
|---|---|---|---|---|
| Female | Age | 54 (46, 63.25) | 60 (57, 62) | |
| Female | ChestPain | Typical angina | 4 (5.56%) | 0 (0%) |
| Female | ChestPain | Atypical angina | 16 (22.22%) | 2 (8%) |
| Female | ChestPain | Non-anginal pain | 34 (47.22%) | 1 (4%) |
| Female | ChestPain | Asymptomatic | 18 (25%) | 22 (88%) |
| Female | BP | 130 (119.5, 140) | 140 (130, 158) | |
| Female | Cholesterol | 249 (210.75, 289.5) | 268 (236, 307) | |
| Female | MaximumHR | 159 (146.75, 167.25) | 146 (133, 157) | |
| Female | ExerciseInducedAngina | No | 64 (88.89%) | 11 (44%) |
| Female | ExerciseInducedAngina | Yes | 8 (11.11%) | 14 (56%) |
| Male | Age | 52 (44, 57) | 57.5 (51, 61) | |
| Male | ChestPain | Typical angina | 12 (13.04%) | 7 (6.14%) |
| Male | ChestPain | Atypical angina | 25 (27.17%) | 7 (6.14%) |
| Male | ChestPain | Non-anginal pain | 34 (36.96%) | 17 (14.91%) |
| Male | ChestPain | Asymptomatic | 21 (22.83%) | 83 (72.81%) |
| Male | BP | 130 (120, 140) | 130 (120, 140) | |
| Male | Cholesterol | 229.5 (206.5, 250.75) | 247.5 (212, 282) | |
| Male | MaximumHR | 163 (150, 175.75) | 141 (125, 156) | |
| Male | ExerciseInducedAngina | No | 77 (83.7%) | 52 (45.61%) |
| Male | ExerciseInducedAngina | Yes | 15 (16.3%) | 62 (54.39%) |
Now suppose you want both stratification variables across the columns:
heart_disease %>%
univariate_table(
strata = ~ Sex + HeartDisease
)
| Variable | Level | No | Yes | No | Yes |
|---|---|---|---|---|---|
| Age | 54 (46, 63.25) | 60 (57, 62) | 52 (44, 57) | 57.5 (51, 61) | |
| ChestPain | Typical angina | 4 (5.56%) | 0 (0%) | 12 (13.04%) | 7 (6.14%) |
| ChestPain | Atypical angina | 16 (22.22%) | 2 (8%) | 25 (27.17%) | 7 (6.14%) |
| ChestPain | Non-anginal pain | 34 (47.22%) | 1 (4%) | 34 (36.96%) | 17 (14.91%) |
| ChestPain | Asymptomatic | 18 (25%) | 22 (88%) | 21 (22.83%) | 83 (72.81%) |
| BP | 130 (119.5, 140) | 140 (130, 158) | 130 (120, 140) | 130 (120, 140) | |
| Cholesterol | 249 (210.75, 289.5) | 268 (236, 307) | 229.5 (206.5, 250.75) | 247.5 (212, 282) | |
| MaximumHR | 159 (146.75, 167.25) | 146 (133, 157) | 163 (150, 175.75) | 141 (125, 156) | |
| ExerciseInducedAngina | No | 64 (88.89%) | 11 (44%) | 77 (83.7%) | 52 (45.61%) |
| ExerciseInducedAngina | Yes | 8 (11.11%) | 14 (56%) | 15 (16.3%) | 62 (54.39%) |
The levels will span the columns in a hierarchical fashion depending on their order in the formula:
heart_disease %>%
univariate_table(
strata = ~ HeartDisease + Sex
)
| Variable | Level | Female | Male | Female | Male |
|---|---|---|---|---|---|
| Age | 54 (46, 63.25) | 52 (44, 57) | 60 (57, 62) | 57.5 (51, 61) | |
| ChestPain | Typical angina | 4 (5.56%) | 12 (13.04%) | 0 (0%) | 7 (6.14%) |
| ChestPain | Atypical angina | 16 (22.22%) | 25 (27.17%) | 2 (8%) | 7 (6.14%) |
| ChestPain | Non-anginal pain | 34 (47.22%) | 34 (36.96%) | 1 (4%) | 17 (14.91%) |
| ChestPain | Asymptomatic | 18 (25%) | 21 (22.83%) | 22 (88%) | 83 (72.81%) |
| BP | 130 (119.5, 140) | 130 (120, 140) | 140 (130, 158) | 130 (120, 140) | |
| Cholesterol | 249 (210.75, 289.5) | 229.5 (206.5, 250.75) | 268 (236, 307) | 247.5 (212, 282) | |
| MaximumHR | 159 (146.75, 167.25) | 163 (150, 175.75) | 146 (133, 157) | 141 (125, 156) | |
| ExerciseInducedAngina | No | 64 (88.89%) | 77 (83.7%) | 11 (44%) | 52 (45.61%) |
| ExerciseInducedAngina | Yes | 8 (11.11%) | 15 (16.3%) | 14 (56%) | 62 (54.39%) |
Similarly, the rows also collapse hierarchically:
heart_disease %>%
univariate_table(
strata = HeartDisease + Sex ~ 1
)
| HeartDisease | Sex | Variable | Level | Summary |
|---|---|---|---|---|
| No | Female | Age | 54 (46, 63.25) | |
| No | Female | ChestPain | Typical angina | 4 (5.56%) |
| No | Female | ChestPain | Atypical angina | 16 (22.22%) |
| No | Female | ChestPain | Non-anginal pain | 34 (47.22%) |
| No | Female | ChestPain | Asymptomatic | 18 (25%) |
| No | Female | BP | 130 (119.5, 140) | |
| No | Female | Cholesterol | 249 (210.75, 289.5) | |
| No | Female | MaximumHR | 159 (146.75, 167.25) | |
| No | Female | ExerciseInducedAngina | No | 64 (88.89%) |
| No | Female | ExerciseInducedAngina | Yes | 8 (11.11%) |
| No | Male | Age | 52 (44, 57) | |
| No | Male | ChestPain | Typical angina | 12 (13.04%) |
| No | Male | ChestPain | Atypical angina | 25 (27.17%) |
| No | Male | ChestPain | Non-anginal pain | 34 (36.96%) |
| No | Male | ChestPain | Asymptomatic | 21 (22.83%) |
| No | Male | BP | 130 (120, 140) | |
| No | Male | Cholesterol | 229.5 (206.5, 250.75) | |
| No | Male | MaximumHR | 163 (150, 175.75) | |
| No | Male | ExerciseInducedAngina | No | 77 (83.7%) |
| No | Male | ExerciseInducedAngina | Yes | 15 (16.3%) |
| Yes | Female | Age | 60 (57, 62) | |
| Yes | Female | ChestPain | Typical angina | 0 (0%) |
| Yes | Female | ChestPain | Atypical angina | 2 (8%) |
| Yes | Female | ChestPain | Non-anginal pain | 1 (4%) |
| Yes | Female | ChestPain | Asymptomatic | 22 (88%) |
| Yes | Female | BP | 140 (130, 158) | |
| Yes | Female | Cholesterol | 268 (236, 307) | |
| Yes | Female | MaximumHR | 146 (133, 157) | |
| Yes | Female | ExerciseInducedAngina | No | 11 (44%) |
| Yes | Female | ExerciseInducedAngina | Yes | 14 (56%) |
| Yes | Male | Age | 57.5 (51, 61) | |
| Yes | Male | ChestPain | Typical angina | 7 (6.14%) |
| Yes | Male | ChestPain | Atypical angina | 7 (6.14%) |
| Yes | Male | ChestPain | Non-anginal pain | 17 (14.91%) |
| Yes | Male | ChestPain | Asymptomatic | 83 (72.81%) |
| Yes | Male | BP | 130 (120, 140) | |
| Yes | Male | Cholesterol | 247.5 (212, 282) | |
| Yes | Male | MaximumHR | 141 (125, 156) | |
| Yes | Male | ExerciseInducedAngina | No | 52 (45.61%) |
| Yes | Male | ExerciseInducedAngina | Yes | 62 (54.39%) |
You can use any of the functionality described in the previous section with stratification variables as well:
heart_disease %>%
univariate_table(
strata = ~ Sex + HeartDisease,
numeric_summary =
c(
`Mean (SD)` = "mean (sd)"
),
categorical_summary =
c(
`Count (%)` = "count (percent%)"
)
)
| Variable | Level | Mean (SD) | Count (%) | Mean (SD) | Count (%) | Mean (SD) | Count (%) | Mean (SD) | Count (%) |
|---|---|---|---|---|---|---|---|---|---|
| Age | 54.56 (10.27) | 59.08 (4.86) | 51.04 (8.62) | 56.09 (8.39) | |||||
| ChestPain | Typical angina | 4 (5.56%) | 0 (0%) | 12 (13.04%) | 7 (6.14%) | ||||
| ChestPain | Atypical angina | 16 (22.22%) | 2 (8%) | 25 (27.17%) | 7 (6.14%) | ||||
| ChestPain | Non-anginal pain | 34 (47.22%) | 1 (4%) | 34 (36.96%) | 17 (14.91%) | ||||
| ChestPain | Asymptomatic | 18 (25%) | 22 (88%) | 21 (22.83%) | 83 (72.81%) | ||||
| BP | 128.74 (16.54) | 146.6 (21.12) | 129.65 (16.02) | 131.93 (17.22) | |||||
| Cholesterol | 256.75 (66.22) | 276.16 (59.88) | 231.6 (37.64) | 246.06 (45.44) | |||||
| MaximumHR | 154.03 (19.25) | 143.16 (20.18) | 161.78 (18.56) | 138.4 (23.08) | |||||
| ExerciseInducedAngina | No | 64 (88.89%) | 11 (44%) | 77 (83.7%) | 52 (45.61%) | ||||
| ExerciseInducedAngina | Yes | 8 (11.11%) | 14 (56%) | 15 (16.3%) | 62 (54.39%) |
The summary columns simply get added to the column-spanning hierarchy.
The add_n
argument will add the sample size to the label
for the stratification group:
heart_disease %>%
univariate_table(
strata = ~ Sex,
add_n = TRUE
)
| Variable | Level | Female (N=97) | Male (N=206) |
|---|---|---|---|
| Age | 57 (50, 63) | 54.5 (47, 59.75) | |
| ChestPain | Typical angina | 4 (4.12%) | 19 (9.22%) |
| ChestPain | Atypical angina | 18 (18.56%) | 32 (15.53%) |
| ChestPain | Non-anginal pain | 35 (36.08%) | 51 (24.76%) |
| ChestPain | Asymptomatic | 40 (41.24%) | 104 (50.49%) |
| BP | 132 (120, 140) | 130 (120, 140) | |
| Cholesterol | 254 (215, 302) | 235 (208.75, 268.5) | |
| MaximumHR | 157 (142, 165) | 150.5 (132, 167.5) | |
| ExerciseInducedAngina | No | 75 (77.32%) | 129 (62.62%) |
| ExerciseInducedAngina | Yes | 22 (22.68%) | 77 (37.38%) |
| HeartDisease | No | 72 (74.23%) | 92 (44.66%) |
| HeartDisease | Yes | 25 (25.77%) | 114 (55.34%) |
When multiple stratification variables are added on one side of the formula, the sample size will show up on the lowest level of the hierarchy, excluding summary columns:
heart_disease %>%
univariate_table(
strata = ~ Sex + HeartDisease,
add_n = TRUE
)
| Variable | Level | No (N=72) | Yes (N=25) | No (N=92) | Yes (N=114) |
|---|---|---|---|---|---|
| Age | 54 (46, 63.25) | 60 (57, 62) | 52 (44, 57) | 57.5 (51, 61) | |
| ChestPain | Typical angina | 4 (5.56%) | 0 (0%) | 12 (13.04%) | 7 (6.14%) |
| ChestPain | Atypical angina | 16 (22.22%) | 2 (8%) | 25 (27.17%) | 7 (6.14%) |
| ChestPain | Non-anginal pain | 34 (47.22%) | 1 (4%) | 34 (36.96%) | 17 (14.91%) |
| ChestPain | Asymptomatic | 18 (25%) | 22 (88%) | 21 (22.83%) | 83 (72.81%) |
| BP | 130 (119.5, 140) | 140 (130, 158) | 130 (120, 140) | 130 (120, 140) | |
| Cholesterol | 249 (210.75, 289.5) | 268 (236, 307) | 229.5 (206.5, 250.75) | 247.5 (212, 282) | |
| MaximumHR | 159 (146.75, 167.25) | 146 (133, 157) | 163 (150, 175.75) | 141 (125, 156) | |
| ExerciseInducedAngina | No | 64 (88.89%) | 11 (44%) | 77 (83.7%) | 52 (45.61%) |
| ExerciseInducedAngina | Yes | 8 (11.11%) | 14 (56%) | 15 (16.3%) | 62 (54.39%) |
A limitation is that when sample size is added in the presence of row and column strata, it is displayed for the marginal groups only:
heart_disease %>%
univariate_table(
strata = Sex ~ HeartDisease,
add_n = TRUE
)
| Sex | Variable | Level | No (N=164) | Yes (N=139) |
|---|---|---|---|---|
| Female (N=97) | Age | 54 (46, 63.25) | 60 (57, 62) | |
| Female (N=97) | ChestPain | Typical angina | 4 (5.56%) | 0 (0%) |
| Female (N=97) | ChestPain | Atypical angina | 16 (22.22%) | 2 (8%) |
| Female (N=97) | ChestPain | Non-anginal pain | 34 (47.22%) | 1 (4%) |
| Female (N=97) | ChestPain | Asymptomatic | 18 (25%) | 22 (88%) |
| Female (N=97) | BP | 130 (119.5, 140) | 140 (130, 158) | |
| Female (N=97) | Cholesterol | 249 (210.75, 289.5) | 268 (236, 307) | |
| Female (N=97) | MaximumHR | 159 (146.75, 167.25) | 146 (133, 157) | |
| Female (N=97) | ExerciseInducedAngina | No | 64 (88.89%) | 11 (44%) |
| Female (N=97) | ExerciseInducedAngina | Yes | 8 (11.11%) | 14 (56%) |
| Male (N=206) | Age | 52 (44, 57) | 57.5 (51, 61) | |
| Male (N=206) | ChestPain | Typical angina | 12 (13.04%) | 7 (6.14%) |
| Male (N=206) | ChestPain | Atypical angina | 25 (27.17%) | 7 (6.14%) |
| Male (N=206) | ChestPain | Non-anginal pain | 34 (36.96%) | 17 (14.91%) |
| Male (N=206) | ChestPain | Asymptomatic | 21 (22.83%) | 83 (72.81%) |
| Male (N=206) | BP | 130 (120, 140) | 130 (120, 140) | |
| Male (N=206) | Cholesterol | 229.5 (206.5, 250.75) | 247.5 (212, 282) | |
| Male (N=206) | MaximumHR | 163 (150, 175.75) | 141 (125, 156) | |
| Male (N=206) | ExerciseInducedAngina | No | 77 (83.7%) | 52 (45.61%) |
| Male (N=206) | ExerciseInducedAngina | Yes | 15 (16.3%) | 62 (54.39%) |
Often when a descriptive analysis is stratified by one or more
variables, it is also of interest to add statistics that compare each
variable across the groups. The associations
argument
allows you to add a list containing an unlimited number of functions
that can produce a scalar value to be placed in the table. First, let’s
define a function:
#Function for a p-value
pval <-
function(y, x) {
#For categorical data use Fisher's Exact test
if(some_type(x, "factor")) {
p <- fisher.test(factor(y), factor(x), simulate.p.value = TRUE)$p.value
#Otherwise use Kruskall-Wallis
} else {
p <- kruskal.test(x, factor(y))$p.value
}
ifelse(p < 0.001, "<0.001", as.character(round(p, 2)))
}
The stratification variable will be placed in the second argument of the function(s) provided. Now you can add it to the function call:
heart_disease %>%
univariate_table(
strata = ~ HeartDisease,
associations = list(`P-value` = pval)
)
| Variable | Level | No | Yes | P-value |
|---|---|---|---|---|
| Age | 52 (44.75, 59) | 58 (52, 62) | 0.12 | |
| Sex | <0.001 | |||
| Sex | Female | 72 (43.9%) | 25 (17.99%) | |
| Sex | Male | 92 (56.1%) | 114 (82.01%) | |
| ChestPain | <0.001 | |||
| ChestPain | Typical angina | 16 (9.76%) | 7 (5.04%) | |
| ChestPain | Atypical angina | 41 (25%) | 9 (6.47%) | |
| ChestPain | Non-anginal pain | 68 (41.46%) | 18 (12.95%) | |
| ChestPain | Asymptomatic | 39 (23.78%) | 105 (75.54%) | |
| BP | 130 (120, 140) | 130 (120, 145) | 0.51 | |
| Cholesterol | 234.5 (208.75, 267.25) | 249 (217.5, 283.5) | 0.11 | |
| MaximumHR | 161 (148.75, 172) | 142 (125, 156.5) | 0.08 | |
| ExerciseInducedAngina | <0.001 | |||
| ExerciseInducedAngina | No | 141 (85.98%) | 63 (45.32%) | |
| ExerciseInducedAngina | Yes | 23 (14.02%) | 76 (54.68%) |
The name of function in the list is what becomes the column label.
The comparison will take place across the number of subgroups there are within the column stratification:
heart_disease %>%
univariate_table(
strata = ~ Sex + HeartDisease,
associations = list(`P-value` = pval)
)
| Variable | Level | No | Yes | No | Yes | P-value |
|---|---|---|---|---|---|---|
| Age | 54 (46, 63.25) | 60 (57, 62) | 52 (44, 57) | 57.5 (51, 61) | 0.53 | |
| ChestPain | <0.001 | |||||
| ChestPain | Typical angina | 4 (5.56%) | 0 (0%) | 12 (13.04%) | 7 (6.14%) | |
| ChestPain | Atypical angina | 16 (22.22%) | 2 (8%) | 25 (27.17%) | 7 (6.14%) | |
| ChestPain | Non-anginal pain | 34 (47.22%) | 1 (4%) | 34 (36.96%) | 17 (14.91%) | |
| ChestPain | Asymptomatic | 18 (25%) | 22 (88%) | 21 (22.83%) | 83 (72.81%) | |
| BP | 130 (119.5, 140) | 140 (130, 158) | 130 (120, 140) | 130 (120, 140) | 0.55 | |
| Cholesterol | 249 (210.75, 289.5) | 268 (236, 307) | 229.5 (206.5, 250.75) | 247.5 (212, 282) | 0.11 | |
| MaximumHR | 159 (146.75, 167.25) | 146 (133, 157) | 163 (150, 175.75) | 141 (125, 156) | 0.01 | |
| ExerciseInducedAngina | <0.001 | |||||
| ExerciseInducedAngina | No | 64 (88.89%) | 11 (44%) | 77 (83.7%) | 52 (45.61%) | |
| ExerciseInducedAngina | Yes | 8 (11.11%) | 14 (56%) | 15 (16.3%) | 62 (54.39%) |
However, using a row stratification makes the comparisons be within those groups:
heart_disease %>%
univariate_table(
strata = Sex ~ HeartDisease,
associations = list(`P-value` = pval)
)
| Sex | Variable | Level | No | Yes | P-value |
|---|---|---|---|---|---|
| Female | Age | 54 (46, 63.25) | 60 (57, 62) | 0.17 | |
| Female | ChestPain | <0.001 | |||
| Female | ChestPain | Typical angina | 4 (5.56%) | 0 (0%) | |
| Female | ChestPain | Atypical angina | 16 (22.22%) | 2 (8%) | |
| Female | ChestPain | Non-anginal pain | 34 (47.22%) | 1 (4%) | |
| Female | ChestPain | Asymptomatic | 18 (25%) | 22 (88%) | |
| Female | BP | 130 (119.5, 140) | 140 (130, 158) | 0.37 | |
| Female | Cholesterol | 249 (210.75, 289.5) | 268 (236, 307) | 0.58 | |
| Female | MaximumHR | 159 (146.75, 167.25) | 146 (133, 157) | 0.15 | |
| Female | ExerciseInducedAngina | <0.001 | |||
| Female | ExerciseInducedAngina | No | 64 (88.89%) | 11 (44%) | |
| Female | ExerciseInducedAngina | Yes | 8 (11.11%) | 14 (56%) | |
| Male | Age | 52 (44, 57) | 57.5 (51, 61) | 0.29 | |
| Male | ChestPain | <0.001 | |||
| Male | ChestPain | Typical angina | 12 (13.04%) | 7 (6.14%) | |
| Male | ChestPain | Atypical angina | 25 (27.17%) | 7 (6.14%) | |
| Male | ChestPain | Non-anginal pain | 34 (36.96%) | 17 (14.91%) | |
| Male | ChestPain | Asymptomatic | 21 (22.83%) | 83 (72.81%) | |
| Male | BP | 130 (120, 140) | 130 (120, 140) | 0.71 | |
| Male | Cholesterol | 229.5 (206.5, 250.75) | 247.5 (212, 282) | 0.11 | |
| Male | MaximumHR | 163 (150, 175.75) | 141 (125, 156) | 0.26 | |
| Male | ExerciseInducedAngina | <0.001 | |||
| Male | ExerciseInducedAngina | No | 77 (83.7%) | 52 (45.61%) | |
| Male | ExerciseInducedAngina | Yes | 15 (16.3%) | 62 (54.39%) |
In general, there must be at least one column stratification variable
in order to use association metrics. See
univariate_associations()
for more details on the workhorse
of this functionality.
descriptives()
is the function that drives the
computation behind the statistics for the columns of the input dataset.
Any of its arguments can be passed from univariate_table()
to add further customization.
As noted above, one of the columns did not appear in the table by
default because it was a logical()
type. By default, only
factor()
and numeric()
types are placed into
the result, though there are (at least) three ways to include it:
You could simply just make the column a conformable type outside of the call:
heart_disease %>%
dplyr::mutate(
BloodSugar = factor(BloodSugar)
) %>%
univariate_table()
| Variable | Level | Summary |
|---|---|---|
| Age | 56 (48, 61) | |
| Sex | Female | 97 (32.01%) |
| Sex | Male | 206 (67.99%) |
| ChestPain | Typical angina | 23 (7.59%) |
| ChestPain | Atypical angina | 50 (16.5%) |
| ChestPain | Non-anginal pain | 86 (28.38%) |
| ChestPain | Asymptomatic | 144 (47.52%) |
| BP | 130 (120, 140) | |
| Cholesterol | 241 (211, 275) | |
| BloodSugar | FALSE | 258 (85.15%) |
| BloodSugar | TRUE | 45 (14.85%) |
| MaximumHR | 153 (133.5, 166) | |
| ExerciseInducedAngina | No | 204 (67.33%) |
| ExerciseInducedAngina | Yes | 99 (32.67%) |
| HeartDisease | No | 164 (54.13%) |
| HeartDisease | Yes | 139 (45.87%) |
The _types
arguments allow you to specify the data types
that are to be interpreted by the high-level function call. Let’s allow
logical()
types to be treated as a categorical
variable:
heart_disease %>%
univariate_table(
categorical_types = c("factor", "logical")
)
| Variable | Level | Summary |
|---|---|---|
| Age | 56 (48, 61) | |
| Sex | Female | 97 (32.01%) |
| Sex | Male | 206 (67.99%) |
| ChestPain | Typical angina | 23 (7.59%) |
| ChestPain | Atypical angina | 50 (16.5%) |
| ChestPain | Non-anginal pain | 86 (28.38%) |
| ChestPain | Asymptomatic | 144 (47.52%) |
| BP | 130 (120, 140) | |
| Cholesterol | 241 (211, 275) | |
| BloodSugar | FALSE | 258 (85.15%) |
| BloodSugar | TRUE | 45 (14.85%) |
| MaximumHR | 153 (133.5, 166) | |
| ExerciseInducedAngina | No | 204 (67.33%) |
| ExerciseInducedAngina | Yes | 99 (32.67%) |
| HeartDisease | No | 164 (54.13%) |
| HeartDisease | Yes | 139 (45.87%) |
The most flexible approach would be to define its own set of functions. By default, the data type of anything that is not interpreted as categorical or numeric is considered “other”. There is infrastruce in place to supply functions and summaries in the same manner for these columns.
heart_disease %>%
univariate_table(
f_other = list(count = function(x) table(x)),
other_summary =
c(
Summary = "count"
)
)
| Variable | Level | Summary |
|---|---|---|
| Age | 56 (48, 61) | |
| Sex | Female | 97 (32.01%) |
| Sex | Male | 206 (67.99%) |
| ChestPain | Typical angina | 23 (7.59%) |
| ChestPain | Atypical angina | 50 (16.5%) |
| ChestPain | Non-anginal pain | 86 (28.38%) |
| ChestPain | Asymptomatic | 144 (47.52%) |
| BP | 130 (120, 140) | |
| Cholesterol | 241 (211, 275) | |
| BloodSugar | FALSE | 258 |
| BloodSugar | TRUE | 45 |
| MaximumHR | 153 (133.5, 166) | |
| ExerciseInducedAngina | No | 204 (67.33%) |
| ExerciseInducedAngina | Yes | 99 (32.67%) |
| HeartDisease | No | 164 (54.13%) |
| HeartDisease | Yes | 139 (45.87%) |
You would need to also define functions for the percentages, proportions, etc. to exactly match the other examples.
You can also add custom functions that can be available for numeric or categorical columns:
heart_disease %>%
univariate_table(
categorical_types = NULL,
f_numeric =
list(
cv = ~sd(.x) / mean(.x)
),
numeric_summary =
c(
`Coef. of variation` = "sd / mean = cv"
)
)
| Variable | Coef. of variation |
|---|---|
| Age | 9.04 / 54.44 = 0.17 |
| BP | 17.6 / 131.69 = 0.13 |
| Cholesterol | 51.78 / 246.69 = 0.21 |
| MaximumHR | 22.88 / 149.61 = 0.15 |
The names of functions become the patterns that searched in the string templates.
Finally, we’ll look at a few of the appearance-related arguments. These can be applied with any combination of other arguments.
As mentioned above, the default format for the table is HTML, but you
could choose an alternative with the format
argument:
heart_disease %>%
univariate_table(
format = "none"
)
## # A tibble: 14 × 3
## Variable Level Summary
## <chr> <chr> <chr>
## 1 "Age" "" 56 (48, 61)
## 2 "Sex" "Female" 97 (32.01%)
## 3 "" "Male" 206 (67.99%)
## 4 "ChestPain" "Typical angina" 23 (7.59%)
## 5 "" "Atypical angina" 50 (16.5%)
## 6 "" "Non-anginal pain" 86 (28.38%)
## 7 "" "Asymptomatic" 144 (47.52%)
## 8 "BP" "" 130 (120, 140)
## 9 "Cholesterol" "" 241 (211, 275)
## 10 "MaximumHR" "" 153 (133.5, 166)
## 11 "ExerciseInducedAngina" "No" 204 (67.33%)
## 12 "" "Yes" 99 (32.67%)
## 13 "HeartDisease" "No" 164 (54.13%)
## 14 "" "Yes" 139 (45.87%)
There are also options for
"latex", "pandoc", "markdown"
.
You can use the labels
and levels
arguments
to add clean text to any of the variable or categorical level names, and
the order
argument to change the position of the variables
in the result:
heart_disease %>%
univariate_table(
labels =
c(
Age = "Age (years)",
ChestPain = "Chest pain"
),
levels =
list(
Sex =
c(
Male = "M"
)
),
order =
c(
"BP",
"Age",
"Cholesterol"
)
)
| Variable | Level | Summary |
|---|---|---|
| BP | 130 (120, 140) | |
| Age (years) | 56 (48, 61) | |
| Cholesterol | 241 (211, 275) | |
| Chest pain | Typical angina | 23 (7.59%) |
| Chest pain | Atypical angina | 50 (16.5%) |
| Chest pain | Non-anginal pain | 86 (28.38%) |
| Chest pain | Asymptomatic | 144 (47.52%) |
| ExerciseInducedAngina | No | 204 (67.33%) |
| ExerciseInducedAngina | Yes | 99 (32.67%) |
| HeartDisease | No | 164 (54.13%) |
| HeartDisease | Yes | 139 (45.87%) |
| MaximumHR | 153 (133.5, 166) | |
| Sex | Female | 97 (32.01%) |
| Sex | M | 206 (67.99%) |
Notice you only need to specify values that need to be changed. Also, ordering is done with the original names even when relabeled.
The variableName
and levelName
arguments
are used to change what the headers are for the column names and
categorical levels, while fill_blanks
determines what goes
in empty cells. Finally, the caption
argument specifies
labels the entire table:
heart_disease %>%
univariate_table(
variableName = "THESE ARE VARIABLES",
levelName = "THESE ARE LEVELS",
fill_blanks = "BLANK",
caption = "HERE IS MY CAPTION"
)
| THESE ARE VARIABLES | THESE ARE LEVELS | Summary |
|---|---|---|
| Age | BLANK | 56 (48, 61) |
| Sex | Female | 97 (32.01%) |
| Sex | Male | 206 (67.99%) |
| ChestPain | Typical angina | 23 (7.59%) |
| ChestPain | Atypical angina | 50 (16.5%) |
| ChestPain | Non-anginal pain | 86 (28.38%) |
| ChestPain | Asymptomatic | 144 (47.52%) |
| BP | BLANK | 130 (120, 140) |
| Cholesterol | BLANK | 241 (211, 275) |
| MaximumHR | BLANK | 153 (133.5, 166) |
| ExerciseInducedAngina | No | 204 (67.33%) |
| ExerciseInducedAngina | Yes | 99 (32.67%) |
| HeartDisease | No | 164 (54.13%) |
| HeartDisease | Yes | 139 (45.87%) |


Contributing to Theia¶
We welcome and encourage contributions to Theia, whether they are new features, bug fixes or tests. The Theia mailing list is the best place for all development related discussions. Please consider joining it. If you have an idea for how you’d like to contribute to Theia, please consider emailing the list first to voice your idea. We can help you fine-tune your idea and this will also help avoid duplicate work by somebody else who may be working on the same feature.
Style and Testing¶
We follow Google’s C++ Style Guide and use git for version control. We use Gerrit to review changes before commits. The git repository has been set up with GerritHub so that the repository lives in GitHub, but all reviews are performed with Gerrit.
When contributing substantial new code or bug fixes, please add unit tests to ensure the usage of the code (or to prove the bug is fixed!).
CMake¶
We use CMake to generate makefiles for Theia to maximize the cross-platform usability. If you need to add a new library or a new file to Theia, you will likely need to add that file to the CMakeLists.txt in src/theia (along with a unit test!).
Developing for Theia¶
Much of the instructions that follow in this section were borrowed and modified from the Ceres Solver project.
Download and configure git.
Sign up for a GitHub account. Accounts are free to register.
Clone the Theia git repository from GerritHub.
git clone https://review.gerrithub.io/sweeneychris/Theia
Build Theia, following the instructions in Building Theia Library.
On Mac and Linux, the CMake build will download and enable the Gerrit pre-commit hook automatically. This pre-submit hook creates Change-Id: ... lines in your commits.
If this does not work OR you are on Windows, execute the following in the root directory of the local git repository:
curl -o .git/hooks/commit-msg http://cs.ucsb.edu/~cmsweeney/theia/commit-msg chmod +x .git/hooks/commit-msg
Configure your GerritHub password.
Sign into https://review.gerrithub.io, go to Settings then HTTP Password. If no password exists, select Generate Password. The username and password listed on this page will need to be entered when pushing your changes to the repo for review (see instructions below).
Submitting a change to Theia¶
Make your changes against master or whatever branch you like. Commit your changes as one patch. This is critical for the review process; if you submit a change with multiple commits, each commit will become a separate review in Gerrit.
Push your changes to the Theia GerritHub instance:
git push origin HEAD:refs/for/master
You will likely have to enter your GerritHub username and password. When the push succeeds, the console will display a URL showing the address of the review. Go to the URL and add reviewers; at this point this is only Chris.
Wait for a review.
Once review comments come in, address them. Please reply to each comment in Gerrit, which makes the re-review process easier. After modifying the code in your git instance, don’t make a new commit.  You will have a chance to edit the commit message as well. Push the new commit to Gerrit as before.
Gerrit will use the Change-Id: to match the previous commit with the new one. The review interface retains your original patch, but also shows the new patch.
Publish your responses to the comments, and wait for a new round of reviews.
Before submitting, make sure you are synced to the latest commit in the repo. To do this, simply run the command:
git pull --rebase origin master
This will pull the latest changes without interfering with your current patch.


This document should have text color 'Blue' using the RGB Hexadecimal color value of "0000ff".
This test passes if the color of text above matches the image below.
Note - This test checks for User Agent requirement as per HTML5 spec NOT the author requirement. (This text is not inside body, so text color is default - black)


1 // Copyright 2007, Google Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above 11 // copyright notice, this list of conditions and the following disclaimer 12 // in the documentation and/or other materials provided with the 13 // distribution. 14 // * Neither the name of Google Inc. nor the names of its 15 // contributors may be used to endorse or promote products derived from 16 // this software without specific prior written permission. 17 // 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30 // 31 // Utilities for testing Google Test itself and code that uses Google Test 32 // (e.g. frameworks built on top of Google Test). 33 34 // GOOGLETEST_CM0004 DO NOT DELETE 35 36 #ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ 37 #define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ 38 39 #include "gtest/gtest.h" 40 41 GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ 42 /* class A needs to have dll-interface to be used by clients of class B */) 43 44 namespace testing { 45 46 // This helper class can be used to mock out Google Test failure reporting 47 // so that we can test Google Test or code that builds on Google Test. 48 // 49 // An object of this class appends a TestPartResult object to the 50 // TestPartResultArray object given in the constructor whenever a Google Test 51 // failure is reported. It can either intercept only failures that are 52 // generated in the same thread that created this object or it can intercept 53 // all generated failures. The scope of this mock object can be controlled with 54 // the second argument to the two arguments constructor. 55 class GTEST_API_ ScopedFakeTestPartResultReporter 56 : public TestPartResultReporterInterface { 57 public: 58 // The two possible mocking modes of this object. 59 enum InterceptMode { 60 INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. 61 INTERCEPT_ALL_THREADS // Intercepts all failures. 62 }; 63 64 // The c'tor sets this object as the test part result reporter used 65 // by Google Test. The 'result' parameter specifies where to report the 66 // results. This reporter will only catch failures generated in the current 67 // thread. DEPRECATED 68 explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); 69 70 // Same as above, but you can choose the interception scope of this object. 71 ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, 72 TestPartResultArray* result); 73 74 // The d'tor restores the previous test part result reporter. 75 virtual ~ScopedFakeTestPartResultReporter(); 76 77 // Appends the TestPartResult object to the TestPartResultArray 78 // received in the constructor. 79 // 80 // This method is from the TestPartResultReporterInterface 81 // interface. 82 virtual void ReportTestPartResult(const TestPartResult& result); 83 private: 84 void Init(); 85 86 const InterceptMode intercept_mode_; 87 TestPartResultReporterInterface* old_reporter_; 88 TestPartResultArray* const result_; 89 90 GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); 91 }; 92 93 namespace internal { 94 95 // A helper class for implementing EXPECT_FATAL_FAILURE() and 96 // EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given 97 // TestPartResultArray contains exactly one failure that has the given 98 // type and contains the given substring. If that's not the case, a 99 // non-fatal failure will be generated. 100 class GTEST_API_ SingleFailureChecker { 101 public: 102 // The constructor remembers the arguments. 103 SingleFailureChecker(const TestPartResultArray* results, 104 TestPartResult::Type type, const std::string& substr); 105 ~SingleFailureChecker(); 106 private: 107 const TestPartResultArray* const results_; 108 const TestPartResult::Type type_; 109 const std::string substr_; 110 111 GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); 112 }; 113 114 } // namespace internal 115 116 } // namespace testing 117 118 GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 119 120 // A set of macros for testing Google Test assertions or code that's expected 121 // to generate Google Test fatal failures. It verifies that the given 122 // statement will cause exactly one fatal Google Test failure with 'substr' 123 // being part of the failure message. 124 // 125 // There are two different versions of this macro. EXPECT_FATAL_FAILURE only 126 // affects and considers failures generated in the current thread and 127 // EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. 128 // 129 // The verification of the assertion is done correctly even when the statement 130 // throws an exception or aborts the current function. 131 // 132 // Known restrictions: 133 // - 'statement' cannot reference local non-static variables or 134 // non-static members of the current object. 135 // - 'statement' cannot return a value. 136 // - You cannot stream a failure message to this macro. 137 // 138 // Note that even though the implementations of the following two 139 // macros are much alike, we cannot refactor them to use a common 140 // helper macro, due to some peculiarity in how the preprocessor 141 // works. The AcceptsMacroThatExpandsToUnprotectedComma test in 142 // gtest_unittest.cc will fail to compile if we do that. 143 #define EXPECT_FATAL_FAILURE(statement, substr) \ 144 do { \ 145 class GTestExpectFatalFailureHelper {\ 146 public:\ 147 static void Execute() { statement; }\ 148 };\ 149 ::testing::TestPartResultArray gtest_failures;\ 150 ::testing::internal::SingleFailureChecker gtest_checker(\ 151 >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ 152 {\ 153 ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ 154 ::testing::ScopedFakeTestPartResultReporter:: \ 155 INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ 156 GTestExpectFatalFailureHelper::Execute();\ 157 }\ 158 } while (::testing::internal::AlwaysFalse()) 159 160 #define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ 161 do { \ 162 class GTestExpectFatalFailureHelper {\ 163 public:\ 164 static void Execute() { statement; }\ 165 };\ 166 ::testing::TestPartResultArray gtest_failures;\ 167 ::testing::internal::SingleFailureChecker gtest_checker(\ 168 >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ 169 {\ 170 ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ 171 ::testing::ScopedFakeTestPartResultReporter:: \ 172 INTERCEPT_ALL_THREADS, >est_failures);\ 173 GTestExpectFatalFailureHelper::Execute();\ 174 }\ 175 } while (::testing::internal::AlwaysFalse()) 176 177 // A macro for testing Google Test assertions or code that's expected to 178 // generate Google Test non-fatal failures. It asserts that the given 179 // statement will cause exactly one non-fatal Google Test failure with 'substr' 180 // being part of the failure message. 181 // 182 // There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only 183 // affects and considers failures generated in the current thread and 184 // EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. 185 // 186 // 'statement' is allowed to reference local variables and members of 187 // the current object. 188 // 189 // The verification of the assertion is done correctly even when the statement 190 // throws an exception or aborts the current function. 191 // 192 // Known restrictions: 193 // - You cannot stream a failure message to this macro. 194 // 195 // Note that even though the implementations of the following two 196 // macros are much alike, we cannot refactor them to use a common 197 // helper macro, due to some peculiarity in how the preprocessor 198 // works. If we do that, the code won't compile when the user gives 199 // EXPECT_NONFATAL_FAILURE() a statement that contains a macro that 200 // expands to code containing an unprotected comma. The 201 // AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc 202 // catches that. 203 // 204 // For the same reason, we have to write 205 // if (::testing::internal::AlwaysTrue()) { statement; } 206 // instead of 207 // GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) 208 // to avoid an MSVC warning on unreachable code. 209 #define EXPECT_NONFATAL_FAILURE(statement, substr) \ 210 do {\ 211 ::testing::TestPartResultArray gtest_failures;\ 212 ::testing::internal::SingleFailureChecker gtest_checker(\ 213 >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ 214 (substr));\ 215 {\ 216 ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ 217 ::testing::ScopedFakeTestPartResultReporter:: \ 218 INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ 219 if (::testing::internal::AlwaysTrue()) { statement; }\ 220 }\ 221 } while (::testing::internal::AlwaysFalse()) 222 223 #define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ 224 do {\ 225 ::testing::TestPartResultArray gtest_failures;\ 226 ::testing::internal::SingleFailureChecker gtest_checker(\ 227 >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ 228 (substr));\ 229 {\ 230 ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ 231 ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ 232 >est_failures);\ 233 if (::testing::internal::AlwaysTrue()) { statement; }\ 234 }\ 235 } while (::testing::internal::AlwaysFalse()) 236 237 #endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_


|
Copley Motion Library
|
Asymmetric S-curve profile generator. More...
Public Member Functions | |
| TrjScurve () | |
| S-curve trajectory default constructor. More... | |
| void | SetStartPos (uunit s) |
| Set the trajectory starting position. More... | |
| uunit | GetStartPos (void) |
| Return the current starting position of the trajectory. More... | |
| const Error * | Calculate (uunit start, uunit end, uunit vel, uunit acc, uunit dec, uunit jrk) |
| Calculate a new S-curve profile and also set it's starting position. More... | |
| const Error * | Calculate (uunit dist, uunit vel, uunit acc, uunit dec, uunit jrk) |
| Calculate a new S-curve profile. More... | |
| const Error * | StartNew (void) |
| Reset this object so it may be passed to an amplifier. More... | |
| void | Finish (void) |
| Notify the trajectory object that it is no longer in use. | |
| const Error * | NextSegment (uunit &pos, uunit &vel, uint8 &time) |
| Get the next PVT segment for this s-curve profile. | |
| Public Member Functions inherited from Trajectory | |
| virtual | ~Trajectory () |
| Virtual destructor. | |
| virtual bool | UseVelocityInfo (void) |
| This function indicates whether the velocity information returned by NextSegment should be used. More... | |
| virtual int | MaximumBufferPointsToUse (void) |
| This function allows a trajectory object to effectively reduce the size of the amplifier's internal trajectory buffer. More... | |
| Public Member Functions inherited from RefObj | |
| RefObj (const char *name=0) | |
| Default constructor for a reference object. More... | |
| virtual | ~RefObj () |
| Reference object destructor. More... | |
| void | SetRefName (const char *name) |
| Assign a name to this reference. More... | |
| uint32 | GrabRef (void) |
| Grab a reference to this object. More... | |
| void | setAutoDelete (bool autoDeleteEna) |
| This function is used to enable or disable the autodelete function for a reference object. More... | |
| void | UnlockRef (void) |
| Unlock an object that was previously locked using RefObj::Lock. | |
Additional Inherited Members | |
| Static Public Member Functions inherited from RefObj | |
| static void | ReleaseRef (uint32 id) |
| Release the local reference to this object. More... | |
| static RefObj * | LockRef (uint32 id) |
| Find the object associated with the passed reference number and lock it to prevent the object from being destroyed while I'm accessing it. More... | |
| static void | LogRefs (void) |
| This function is provided for debugging. More... | |
| Protected Member Functions inherited from RefObj | |
| void | KillRef (void) |
| Destroy this reference. More... | |
Asymmetric S-curve profile generator.
A symmetric S-curve profile uses the same constraint for acceleration & deceleration. Asymmetric profiles use different acceleration & deceleration values. Copley amplifiers are able to calculate symmetric profiles internally, however if asymmetric s-curve profiles are required then they must be calculate external to the amplifier, and passed to it using PVT profile mode.
This class extends the generic Trajectory class, and provides the code necessary to calculate an asymmetric s-curve profile. Since it extends the Trajectory object, it may be passed to the Amp::SendTrajectory function.
Internally, the s-curve profile is stored as an absolute move from some starting position. This allows the same trajectory object to be reused for multiple moves of the same distance from different starting positions. The starting position may be either passed to the TrjScurve::Calculate function, or set using TrjScurve::SetStartPos.
| TrjScurve | ( | void | ) |
S-curve trajectory default constructor.
This simply sets the profile to zero length with a starting position of zero.
Calculate a new S-curve profile and also set it's starting position.
| start | The profile starting position. |
| end | The profile ending position. |
| vel | The maximum allowable velocity for the move. |
| acc | The maximum allowable acceleration for the move. |
| dec | The maximum allowable deceleration for the move. |
| jrk | The maximum jerk (rate of change of acceleration) for the move. |
Calculate a new S-curve profile.
The resulting profile may then be sent to an Amp object using the Amp::SendTrajectory method.
Note, all profile parameters are passed in 'user units'. See the documentation for Amp::SetCountsPerUnit for details.
Note also that this function calculate the profile as an absolte move from the starting position that is set using TrjScurve::SetStartPos. The same profile may be used multiple times with different starting positions without calling the TrjScurve::Calculate function again.
| dist | The distance to move. |
| maxVel | The maximum allowable velocity for the move. |
| maxAcc | The maximum allowable acceleration for the move. |
| maxDec | The maximum allowable deceleration for the move. |
| maxJrk | The maximum jerk (rate of change of acceleration) for the move. |
The distance will always be met exactly. This may be either positive or negative.
The velocity, acceleration, deceleration and jerk values are constraints and won't be exceeded. These must all be positive numbers greater then zero.
| uunit GetStartPos | ( | void | ) |
Return the current starting position of the trajectory.
The starting position will either be the value set using TrjScurve::SetStartPos, or the value set using TrjScurve::Calculate. If neither has been called since construction, then the starting position will be zero.
| void SetStartPos | ( | uunit | s | ) |
Set the trajectory starting position.
S-curve profiles are internally stored as absolute moves of some length. This allows them to be used multiple times with different starting positions.
This function may be used to update the starting position of the trajectory.
| s | The new starting position |
|
virtual |
Reset this object so it may be passed to an amplifier.
This will return an error if the trajectory has not yet been calculated, or if it is currently being sent to another amp.
Reimplemented from Trajectory.


A Novel LSTM based model which uses "Association Learning" was used to predict resource usage in cloud machines. Model was validated across multiple datasets and found to perform better than other state-of-the-art models.
Technologies used: Keras, Tensorflow, Python, Docker
Published at: 2018 International Joint Conference on Neural Networks (IJCNN)
A two stage novel model was built where in 1st stage, hybrid of long short term memory (LSTM) and bidirectional long short term memory (BLSTM) is used to predict future resource usage. In 2nd stage, LSTM classification model was build to classify expected state as normal or abnormal.
Technologies used: Keras, Tensorflow, Python, Docker
Published at: 2017 14th IEEE India Council International Conference (INDICON)
A modified kernel that can allow user to encrypt files on-the-fly, so overall user experience remains seamless whereas actually the file remains encrypted on the disk.
Each data block is encrypted and stored on write() and decrypted on read(). Kernel read() and write() functions were modified for this purpose. Encryptor was tested on ciphers where size of encrypted data may differ from that of plaintext.
Technologies used: Linux kernel, Linux Virtual File System
Clustered MariaDb was used as the database managemnet system. Database was tested on exact search queries, range queries, index queries, etc. Indexing was done on the database to enhance query performance.
Technologies used: MariaDb, Galera cluster
This was a social as well as technical project. Several surveys were conducted to identify root cause of lack of seat belt awareness. Built a prototype which can track whether passengers in car are wearing seat belts. The seat belt usage data is futher sent over cloud. The data on cloud can be accessed by governing agencies to track seat belt usage.
Technologies used: IR sensors, GSM modeule, accelerometer, MySQL
IOT device for monitoring usage of LPG gas cylinders along with leakage alerts through Email and SMS
A web portal for users to view and book institute vehicles (cabs, buses, etc.)
Technologies used: MySQL, PHP, LAMP, JavaScript, HTML, CSS
The portal provides facility for online secure money transactions, creating accounts and checking balance.
Technologies used: MySQL, PHP, Python, JavaScript, HTML, CSS
Arduino and Piezo sensor based lock. Piezo sensor was used to detect the vibrations. Used Arduino programming for input processing.


Mixtapemonkey Chance The Rapper Coloring Book
Mixtapemonkey Chance The Rapper Acid Rap
Chance The Rapper The Big Day Album Stream Download Listen Now Chance The Rapper First Listen Music Just Jared
Chance The Rapper No Problem Mp3 Free Download Peatix
Download Chance The Rapper Ft Nicki Minaj Lil Durk Slide Around Mp3 Illuminaija
Chance The Rapper Png Png Download Chance The Rapper Transparent Png Download Vhv
Mp3 Download The Return Chance The Rapper Naijaballerz
Chance The Rapper Coloring Book 1920x1080 Download Hd Wallpaper Wallpapertip
Chance The Rapper V Chance The Rapper Transparent Hd Png Download 1088x880 1610972 Pngfind
Download Album Chance The Rapper Instagram Ep
Transparent Chance The Rapper Png Png Download Transparent Png Image Pngitem
Chance The Rapper Sticker Chance The Rapper I Love My Wife Hd Png Download Vhv
Chance The Rapper Png Images Free Transparent Chance The Rapper Download Kindpng
Download Mp3 Chance The Rapper The Return 042jamz
Chance The Rapper Same Drugs Video 1280x720 Download Hd Wallpaper Wallpapertip
Chance The Rapper The Return Mp3 Download Gistgallery
Chance The Rapper Transparent Png Download Chance The Rapper Transparent Png Download 3575x3443 1345264 Pngfind
Chance The Rapper The Writer Fresh Unreleased Song Free Download This Song Is Sick
Lido And Chance The Rapper Release Remix Of Jeremih S Planes Free Download
Chance The Rapper Man Png Download 456x489 3108340 Png Image Pngjoy
Download Chance The Rapper Coloring Book Zip3 Dogsever
Download Mp3 Chance The Rapper The Heart The Tongue Video Lyrics Neolife International
Image Freeuse Download Chance The Kids T Shirt For Chance The Rapper Png Cartoon Transparent Png 600x600 1611281 Pngfind
3
Chance The Rapper Chance The Rapper Hd Png Download 1000x1000 14498560 Png Image Pngjoy
Chance The Rapper Reveals First Week Download Numbers For New Album Surf News Diy
1500x1950 Acid Rap Chance The Rapper Wallpaper Download Billboard Magazine Chance The Rapper 1500x1950 Wallpaper Teahub Io
Merry Christmas Lil Mama Re Wrapped Chance The Rapper
Download Mp3 Vic Mensa Wyclef Jean Chance The Rapper Shelter Vevosongs
Mp3 Download Brandy Ft Chance The Rapper Baby Mama
Download Mp3 Chance The Rapper All We Got Og Ft Kanye West 320kbps Lyrics M4a Mp4 Bazenation
Video Justin Bieber Ft Chance The Rapper Holy Naijabasic Com


|
MINI55_BSP V3.02.004
The Board Support Package for Mini55 Series MCU
|
Functions | |
| static __INLINE int32_t | HDIV_Div (int32_t x, int16_t y) |
| Division function to calculate (x/y) More... | |
| static __INLINE int16_t | HDIV_Mod (int32_t x, int16_t y) |
| To calculate the remainder of x/y, i.e., the result of x mod y. More... | |
|
static |
|
static |


{% extends "base.html" %} {% block title %}Edit term{% endblock %} {% block breadcrumbs %} View book View occurrence


About Me
Hello! My name is Tyler Mills, and I have been dabbling in computer programming for as long as I've had a computer. I started out creating simple HTML and CSS websites for video games I played in my youth, growing into building automation scripts in basic languages like AutoIt.
After getting my undergraduate degree in business, I decided to attend law school. In my third year of law school I stumbled across Python and was instantly enamoured by the ability to quickly develop programs for automation, and interacting with remote systems. During this time I started a business that worked in the leased server space, provisions servers for clients all over the world. I built custom Python scripts to trigger overall setup and specific program installations on clients machines using XML-RPC.
Hello! My name is Tyler Mills, and I have been dabbling in computer programming for as long as I've had a computer. I started out creating simple HTML and CSS websites for video games I played in my youth, growing into building automation scripts in basic languages like AutoIt.
After getting my undergraduate degree in business, I decided to attend law school. In my third year of law school I stumbled across Python and was instantly enamoured by the ability to quickly develop programs for automation, and interacting with remote systems. During this time I started a business that worked in the leased server space, provisions servers for clients all over the world. I built custom Python scripts to trigger overall setup and specific program installations on clients machines using XML-RPC.
After moving on from the server provisioning business, I decided to get back into web development, and stepped into a Web Design/Administrator role a small real estate start up to develop a MLS-like front end using a LAMP stack, as well as Python for daily automation on the back end, which fed reporting and client facing statists on properties.
Post real-estate startup, I took a role with a large financial services provider as a Data Analyst, progressing to a Data Manager role, overseeing a team of data analysts responsible for maintaining two of the largest datasets on the product.
Now, after gaining important skills in data analysis, as well as people management and strategic planning, I have deciced to pursue my passion in programming as a full-stack developer using the MERN stack.
Follow along on my journey to become a full-stack developer on GitHub: tmills-2012
Projects
Enhanced Bus Tracking
This project explores an exercise in several key components of web development, such as API calls, asynchronous functions, and updating the user view as new data is captured.
This project delves into the idea of page bounds and replacing images based on directio of travel. Initially PacMan is moving to the right, alternating between open and closed mouth images. Upon hitting the right page bound, PacMan will reverse direction, swaping images for left facing PacMan, and proceed to move across the page to the left...
This project tested our ability to access browser and DOM elements to cause a pair of eyes to move along with the updated position of the mouse...


I have yet to try any of the timebox or habit tecnniques from these blog posts. I'm actually wishing I had read through this issue first from Waffle.io! I'll learn to read through all of the cards before starting on them in my next sprint.
However, I have successfully used Habitica for tracking my tasks and improving my habits! I've also been using Toggl to start building awareness of how much time I am spending and using on a task.
Even better news: Habitica and Toggl integrate with each other! So I can use Habitica to build tasks, habits and intrinsic rewards, and Toggl to timebox my effort on those tasks as well!
I found BJ Fogg's observation about tracking weight resonated with me incredibly well. When BJ was weighing himself every day, he was subconsciouly keeping himself on track with a feedback loop. When he stopped weighing himself, his weight shot up! Fun fact: I have experienced this EXACT predicament! This is the power of habits and feedback loops!
Lesson learned: get back to weighing myself, every day!
I really like the idea behind the Pomodoro technique. I'm going to try doing that at work this week.
I also will try meditating as a daily habit. I downloaded the Headspace app which I will use to coach me this week.


The foreign exchange (also known as FX or forex) market is a global marketplace for exchanging national currencies against one another. Because of the worldwide reach of trade, commerce, and finance, forex markets tend to be the largest and most liquid asset markets in the world.
We buy a currency when we expect the currency to appreciate and sell it at a higher price later to earn a profit. Similarly, we sell a currency when we expect the currency to depreciate and buy it back at a lower price to earn a profit.


Dwolla OAuth 2.0
Overview
Dwolla's API lets you interact with a user's Dwolla account and act on its behalf to send money, request money, and more. To do so, your application first needs to request authorization from users.
Dwolla implements the OAuth 2.0 standard to facilitate this authorization. Similar to Facebook and Twitter's authentication flow, the user is first presented with a permission dialog for your application, at which point the user can either approve the permissions requested, or reject them. Once the user approves, an authorization_code
is sent to your application, which will then be exchanged for an OAuth access_token
.
User experience
Here's a typical user experience of Dwolla's OAuth + REST API:
Customer initiates a checkout on the merchant site, and clicks on a "Pay with Dwolla" button, which sends the user to the OAuth request authorization URL. If the user doesn't have a Dwolla account, they can create one during the OAuth process. Read more about OAuth account creation.
OAuth permissions dialog pops up.
Customer inputs their PIN. The application then uses this
pin
along with the user'saccess_token
to Send money to a merchant account.
OAuth flow
First, redirect the user to the permission page:
Generate a URL which describes the permissions your application requires (scope
), who the client application is (client_id
), and where the user should be redirected to after they grant or deny permissions to your application (redirect_uri
). If the user doesn't have a Dwolla account, they can create one during the OAuth process. Read more about OAuth account creation.
require 'dwolla'
# set your application credentials
Dwolla::api_key = @api_key
Dwolla::api_secret = @api_secret
# permissions to request
scope = "send|balance|funding|transactions|accountinfofull"
redirect_uri = "https://www.back2ursite.com/return"
authUrl = Dwolla::OAuth.get_auth_url(redirect_uri, scope)
puts authUrl
# => https://www.dwolla.com/oauth/v2/authenticate?client_id=JCGQXLrlfuOqdUYdTcLz3rBiCZQDRvdWIUPkw%2B%2BGMuGhkem9Bo&response_type=code&redirect_uri=www.back2ursite.com%2Freturn&scope=send|balance|funding|transactions|accountinfofull
require '../lib/dwolla.php';
// URL where user is sent after granting authorization
$redirectUri = 'https://www.back2ursite.com/return'
// set desired permissions
$permissions = array("Send", "Transactions", "Balance", "Request", "Contacts", "AccountInfoFull", "Funding");
$Dwolla = new DwollaRestClient($apiKey, $apiSecret, $redirectUri, $permissions);
$authUrl = $Dwolla->getAuthUrl();
echo $authUrl;
// => https://www.dwolla.com/oauth/v2/authenticate?client_id=JCGQXLrlfuOqdUYdTcLz3rBiCZQDRvdWIUPkw%2B%2BGMuGhkem9Bo&response_type=code&redirect_uri=www.back2ursite.com%2Freturn&scope=send|balance|funding|transactions|accountinfofull
var Dwolla = require('dwolla')('app key here', 'app secret here');
// URL where user is sent after granting (or denying) authorization
var redirect_uri = 'https://www.back2ursite.com/return';
// permissions to request
var scope = 'send|balance|funding|transactions|accountinfofull';
var authUrl = Dwolla.authUrl(redirect_uri, scope);
console.log(authUrl);
// => https://www.dwolla.com/oauth/v2/authenticate?client_id=JCGQXLrlfuOqdUYdTcLz3rBiCZQDRvdWIUPkw%2B%2BGMuGhkem9Bo&response_type=code&redirect_uri=www.back2ursite.com%2Freturn&scope=send|balance|funding|transactions|accountinfofull
from dwolla import DwollaClientApp
Dwolla = DwollaClientApp('app key here', 'app secret here')
# URL where user is sent after granting authorization
redirect_uri = "https://www.back2ursite.com/return"
# permissions to request
scope = "send|balance|funding|transactions|accountinfofull"
authUrl = Dwolla.init_oauth_url(redirect_uri, scope)
print authUrl
# => https://www.dwolla.com/oauth/v2/authenticate?client_id=JCGQXLrlfuOqdUYdTcLz3rBiCZQDRvdWIUPkw%2B%2BGMuGhkem9Bo&response_type=code&redirect_uri=www.back2ursite.com%2Freturn&scope=send|balance|funding|transactions|accountinfofull
Request Format:
https://www.dwolla.com/oauth/v2/authenticate?client_id={client_id}&response_type=code&redirect_uri={redirect_uri}&scope={scope}
Request Example:
https://www.dwolla.com/oauth/v2/authenticate?client_id=abcdefg&response_type=code&redirect_uri=https%3A%2F%2Fwww.myredirect.com%2Fredirect&scope=send%7Ctransactions
If the user isn't yet logged in, they'll be asked to log in:
Here's what the permissions dialog looks like.
Second, the user will be redirected back to your site.
The user will be taken back to the supplied redirect_uri, along with a temporary code
as a GET querystring parameter. Note: This is not an access token, yet. You will need to exchange this temporary authorization code for an actual access_token
& refresh_token
pair in the next step.
In a successful approval:
{redirect_url}?code={code}
In a failure, or rejection:
{redirect_url}?error=access_denied&error_description=The+user+denied+the+request.
For example, if I set the redirect_uri
to https://www.google.com/
...
Third, exchange the code for an access token & refresh token pair
In this final step of the OAuth flow, you will exchange the code
granted on step 2 for an OAuth access_token
and refresh_token
pair. In order to do so, you will need to make an HTTP request from your application server, to the Dwolla authentication server.
POST https://www.dwolla.com/oauth/v2/token
Content-Type: application/json
{
"client_id": "JCGQXLrlfuOqdUYdTcLz3rBiCZQDRvdWIUPkw+pGMuGhkem9Bo",
"client_secret": "g7QLwvO37aN2HoKx1amekWixa2g0AIuPbD5C/JSLqXIcDOxfTr",
"code": "wb1JCUPYz6CDrebgBrtTkxx5pKo=",
"grant_type": "authorization_code",
"redirect_uri": "https://www.google.com/"
}
code = "put authorization code here"
# we'll need the redirect_uri we used in step 1
redirect_uri = "https://www.google.com/"
token = Dwolla::OAuth.get_token(code, redirect_uri)
puts token
# => '9RoFDRNiH8e1d3pOMPyVGSilGqvlhS9qeFFVgMkx0u0PELhECu'
$code = "authorization code goes here";
$token = $Dwolla->requestToken($code);
echo $token
// => "9RoFDRNiH8e1d3pOMPyVGSilGqvlhS9qeFFVgMkx0u0PELhECu"
var code = "put authorization code here";
var redirect = "https://www.google.com/";
Dwolla.finishAuth(code, redirect_uri, function(error, auth) {
if (err) return console.log(error);
console.log(auth);
});
// =>
code = "put authorization code here"
# we'll need the redirect_uri we used in step 1
redirect_uri = "https://www.google.com/"
token = Dwolla.get_oauth_token(code, redirect_uri=redirect_uri)
print token
# =>
Response:
{
"access_token": "aD5dERNABG6LIDwUZ0HLj+BhTxnXLNR3WhrIUObJkUPiqe8Gpc",
"expires_in": 3600,
"refresh_token": "cIwRQqCIZUW3+FUfCNqK6fCaJ2aSRWK2S1I0mhhIvIXg77cv\/X",
"refresh_expires_in": 5184000,
"token_type": "bearer"
}
If there's an error:
{
"error": "access_denied",
"error_description": "Application is suspended."
}
Refreshing an Access Token
Handle token expiration
Because access tokens are short lived and expire in 1 hour, if you wish to maintain authorization for a user account for a duration of time longer than 1 hour, you can use the refresh token to generate a new access token and refresh token. There is no limit to the number of times you can refresh a user's access token. Nonetheless, it is important to keep in mind that upon a successful refresh your existing access token and refresh token will be invalidated.
Note: We recommend safely storing a user's refresh token, i.e. in a database, so you can use it in the future to refresh authorization. If you misplace or delete a refresh token, then you will need to repeat the OAuth flow starting from the first step described above.
To refresh your authorization:
POST https://www.dwolla.com/oauth/v2/token
Content-Type: application/json
{
"client_id": "JCGQXLrlfuOqdUYdTcLz3rBiCZQDRvdWIUPkw+pGMuGhkem9Bo",
"client_secret": "g7QLwvO37aN2HoKx1amekWi8a2g7AIuPbD5C/JSLqXIcDOxfTr",
"refresh_token": "vpr47YDbMqlz7EgRjbCqpIZvGWhdJmXjU9zJz8LHy04/ai6lhC",
"grant_type": "refresh_token"
}
refresh_token = "cIwRQqCIZUW3+FUfCNqK6fCaJ2aSRWK2S1I0mhhIvIXg77cv/X"
puts Dwolla::OAuth.refresh_auth(refresh_token)
$refreshToken = "yURlUzIyYSSP0q+8D9zyJ6wje2BZdFpK4yXG2B69S/+FcUXZVE";
$response = $Dwolla->refreshAuth($refreshToken);
print_r($response)
var refreshToken = 'hYh0mbf98392MKPZp6fldEaT7KiJUTTfmakJzaQWQV4FFn8BhE';
dwolla.refreshAuth('hYh0mbf98392MKPZp6fldEaT7KiJUTTfmakJzaQWQV4FFn8BhE', function(err, res) {
console.log(res);
});
Response:
{
"access_token": "nitnbGDO4N/94ukGsYehXlcpEnBdu7+X4uH9Lv7A71bPp1NIdP",
"expires_in": 3600,
"refresh_token": "wcMyMs+V3CfGgtW9edlanIZ/WZOZzjesBV7C1qTuynMSbk1oqh",
"refresh_expires_in": 5184000,
"token_type": "bearer"
}
OAuth Scopes
When authenticating a user into your Dwolla application, you must explicitly specify which access scopes you will require. This helps users better understand what kind of account permissions you require, and which permissions they are granting you application. Multiple scopes may be scoped in your authorization request using the pipe ("|") character (e.g. scope=send|transactions|etc).
Requested scopes must also be enabled on your application settings, otherwise, authenticating will respond with an error stating that your application requested invalid scopes.
| Scope | Description |
|---|---|
| AccountInfoFull | Detailed information about the authenticated account |
| Contacts | Contacts of the authenticated account |
| Transactions | List and retrieve user's transactions |
| Balance | Dwolla balance of the authenticated account |
| Send | Send money from the authenticated account |
| Request | Initiate a money request, originating from the authenticated account. View, cancel, and fulfill existing requests |
| Funding | View, add, verify Funding sources (i.e. bank accounts) linked to the authenticated account. Initiate deposits from and withdrawals to funding sources |
| ManageAccount | Manage the authenticated user's account settings |


--- layout: post title: When I Think of Him date: '2008-01-06T00:00:00-05:00' tags: - '2008' tumblr_url: http://poetry.liabogoev.com/post/49085607415/when-i-think-of-him ---
When I think of him,
My heart skips a beat,
A jolt of electricity
Running through my veins,
Pulsing,
Tingling,
Making my heart
Flip flop in my giddy chest,
Every time my thoughts stray
To him.
There’s a frog,
A little frog nestled in my stomach,
And it jumps,
Every time
I think of him.
It seems like my body won’t listen to me.
My eyes search for him in the crowd,
My mind wanders to him in the darkness
My arms long to wrap around him.
Every time my thoughts drift
Toward him,
The rest of me
Doesn’t listen.
My mind and my heart
Say two different things.

My feet walk in his direction,
No matter where he is,
Whenever I think
Of him.


What is this even?
This is a website dedicated to short, referenced facts. It is intended to be used as a citations source for use when required.
Facts can be voted on by registered members. Users start with 10 votes, and from then on get one vote per hour to spend.
Users can vote multiple times on a single fact.
To avoid bias, it is never visible who posts a given fact.
Facts will be moderated before they are visible to all users.
hey.fyi is written as a demo web application in the Go programming language.


# of Transactions: {{userToEdit.transactions.length}}
| Quote # | Date of quote | Device | Condition | Quote | Payment Method | Status | |
|---|---|---|---|---|---|---|---|
| {{transaction._id.slice(15)}} | {{transaction.date | date : format : short}} | {{transaction.device}} | {{transaction.condition}} | {{transaction.amount | currency}} | {{transaction.paymentmethod }} | {{transaction.status}} |


SPTAudioStreamingController Class Reference
| Inherits from | NSObject |
| Declared in | SPTAudioStreamingController.h |
Overview
This class manages audio streaming from Spotify.
Note: There must be only one concurrent instance of this class in your app.
Tasks
Initialisation and Setup
-
+ sharedInstance
-
– startWithClientId:audioController:allowCaching:error:
-
– startWithClientId:error:
-
– stopWithError:
-
– loginWithAccessToken:
-
– logout
Properties
-
initialized
property -
loggedIn
property -
delegate
property -
playbackDelegate
property -
diskCache
property
Controlling Playback
-
– setVolume:callback:
-
– setTargetBitrate:callback:
-
– seekTo:callback:
-
– setIsPlaying:callback:
-
– playSpotifyURI:startingWithIndex:startingWithPosition:callback:
-
– queueSpotifyURI:callback:
-
– skipNext:
-
– skipPrevious:
-
– setShuffle:callback:
-
– setRepeat:callback:
-
volume
property -
metadata
property -
playbackState
property -
targetBitrate
property
Properties
delegate
The receiver’s delegate, which deals with session events such as login, logout, errors, etc.
@property (nonatomic, weak) id<SPTAudioStreamingDelegate> delegate
Declared In
SPTAudioStreamingController.h
diskCache
The object responsible for caching of audio data.
@property (nonatomic, strong) id<SPTDiskCaching> diskCache
Discussion
The object is an instance of a class that implements the SPTDiskCaching
protocol.
If nil
, no caching will be performed.
Declared In
SPTAudioStreamingController.h
initialized
YES while the SPTAudioStreamingController
is initialized
@property (atomic, readonly, assign) BOOL initialized
Declared In
SPTAudioStreamingController.h
loggedIn
Returns YES
if the receiver is logged into the Spotify service, otherwise NO
.
@property (atomic, readonly) BOOL loggedIn
Declared In
SPTAudioStreamingController.h
metadata
Metadata for the currently playing context
@property (atomic, readonly) SPTPlaybackMetadata *metadata
Declared In
SPTAudioStreamingController.h
playbackDelegate
The receiver’s playback delegate, which deals with audio playback events.
@property (nonatomic, weak) id<SPTAudioStreamingPlaybackDelegate> playbackDelegate
Declared In
SPTAudioStreamingController.h
playbackState
The players current state
@property (atomic, readonly) SPTPlaybackState *playbackState
Declared In
SPTAudioStreamingController.h
Instance Methods
loginWithAccessToken:
Log into the Spotify service for audio playback.
- (void)loginWithAccessToken:(NSString *)accessToken
Parameters
- accessToken
An authenticated access token authorized with the
streaming
scope.
Discussion
Audio playback will not be available until the receiver is successfully logged in.
Login is asynchronous.
Success will be notified on the audioStreamingDidLogin:
delegate method and
failure will be notified on the audioStreaming:didEncounterError:
delegate method.
Declared In
SPTAudioStreamingController.h
logout
Log out of the Spotify service
- (void)logout
Discussion
This method is asynchronous. When logout is complete you will be notified by the
audioStreamingDidLogout:
delegate method.
Declared In
SPTAudioStreamingController.h
playSpotifyURI:startingWithIndex:startingWithPosition:callback:
Play a Spotify URI.
- (void)playSpotifyURI:(NSString *)spotifyUri startingWithIndex:(NSUInteger)index startingWithPosition:(NSTimeInterval)position callback:(SPTErrorableOperationCallback)block
Parameters
- spotifyUri
The Spotify URI to play.
- index
The index of an item that should be played first, e.g. 0 - for the very first track in the playlist or a single track
- position
starting position for playback in sec
- block
The callback block to be executed when the playback command has been received, which will pass back an
NSError
object if an error ocurred.
Discussion
Supported URI types: Tracks, Albums and Playlists
Declared In
SPTAudioStreamingController.h
queueSpotifyURI:callback:
Queue a Spotify URI.
- (void)queueSpotifyURI:(NSString *)spotifyUri callback:(SPTErrorableOperationCallback)block
Parameters
- spotifyUri
The Spotify URI to queue.
- block
The callback block to be executed when the playback command has been received, which will pass back an
NSError
object if an error ocurred.
Discussion
Supported URI types: Tracks
Declared In
SPTAudioStreamingController.h
seekTo:callback:
Seek playback to a given location in the current track.
- (void)seekTo:(NSTimeInterval)position callback:(SPTErrorableOperationCallback)block
Parameters
- position
in sec to seek to.
- block
The callback block to be executed when the command has been received, which will pass back an
NSError
object if an error ocurred.
See Also
Declared In
SPTAudioStreamingController.h
setIsPlaying:callback:
Set the “playing” status of the receiver.
- (void)setIsPlaying:(BOOL)playing callback:(SPTErrorableOperationCallback)block
Parameters
- playing
Pass
YES
to resume playback, orNO
to pause it.
- block
The callback block to be executed when the command has been received, which will pass back an
NSError
object if an error ocurred.
See Also
Declared In
SPTAudioStreamingController.h
setRepeat:callback:
Set repeat state, on, off or repeat-one
- (void)setRepeat:(SPTRepeatMode)mode callback:(SPTErrorableOperationCallback)block
Parameters
- mode
The state to set, SPTRepeatOff, SPTRepeatContext or SPTRepeatOne.
- block
The callback block to be executed when the command has been received, which will pass back an
NSError
object if an error ocurred.
Declared In
SPTAudioStreamingController.h
setShuffle:callback:
Set state for shuffle, on or off.
- (void)setShuffle:(BOOL)enable callback:(SPTErrorableOperationCallback)block
Parameters
- enable
The state to set, YES to enable shuffle and NO to disable.
- block
The callback block to be executed when the command has been received, which will pass back an
NSError
object if an error ocurred.
Declared In
SPTAudioStreamingController.h
setTargetBitrate:callback:
Set the target streaming bitrate.
- (void)setTargetBitrate:(SPTBitrate)bitrate callback:(SPTErrorableOperationCallback)block
Parameters
- bitrate
The bitrate to target.
- block
The callback block to be executed when the command has been received, which will pass back an
NSError
object if an error ocurred.
Discussion
The library will attempt to stream audio at the given bitrate. If the given bitrate is not available, the closest match will be used. This process is completely transparent, but you should be aware that data usage isn’t guaranteed.
Declared In
SPTAudioStreamingController.h
setVolume:callback:
Set playback volume to the given level.
- (void)setVolume:(SPTVolume)volume callback:(SPTErrorableOperationCallback)block
Parameters
- volume
The volume to change to, as a value between
0.0
and1.0
.
- block
The callback block to be executed when the command has been received, which will pass back an
NSError
object if an error ocurred.
See Also
Declared In
SPTAudioStreamingController.h
skipNext:
Go to the next track in the queue.
- (void)skipNext:(SPTErrorableOperationCallback)block
Parameters
- block
The callback block to be executed when the command has been received, which will pass back an
NSError
object if an error ocurred.
Declared In
SPTAudioStreamingController.h
skipPrevious:
Go to the previous track in the queue
- (void)skipPrevious:(SPTErrorableOperationCallback)block
Parameters
- block
The callback block to be executed when the command has been received, which will pass back an
NSError
object if an error ocurred.
Declared In
SPTAudioStreamingController.h
startWithClientId:audioController:allowCaching:error:
Start the SPAudioStreamingController
thread with a custom audio controller.
- (BOOL)startWithClientId:(NSString *)clientId audioController:(SPTCoreAudioController *)audioController allowCaching:(BOOL)allowCaching error:(NSError *__autoreleasing *)error
Parameters
- clientId
Your client id found at developer.spotify.com
- audioController
Custom audio controller
- allowCaching
YES of persisten disk caching is allowed
- error
If method returns NO, error will be set
Return Value
Returns YES if initialization was successful
Discussion
Note: You MUST initialize the SPAudioStreamingController
sharedInstance before calling any other method.
Declared In
SPTAudioStreamingController.h
startWithClientId:error:
Start the SPAudioStreamingController
thread with the default audioController.
- (BOOL)startWithClientId:(NSString *)clientId error:(NSError *__autoreleasing *)error
Parameters
- clientId
Your client id found at developer.spotify.com
- error
If method returns NO, error will be set
Return Value
Returns YES if initialization was successful
Discussion
Note: You need to start the SPAudioStreamingController
sharedInstance before calling any other method.
Declared In
SPTAudioStreamingController.h


| Project | Number | Date |
|---|---|---|
| Oms-Automation-Framework_M1046847 | 1.8.0 | 20 Sep 2018, 11:43 |
The following graph shows passing and failing statistics for features
| Scenarios | Steps | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Feature | Total | Passed | Failed | Total | Passed | Failed | Skipped | Pending | Undefined | Missing | Duration | Status |
| Leads click on capability to add capability | 1 | 1 | 0 | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 1m 09s 608ms | Passed |
| Admin check for assginment function for OMS | 1 | 1 | 0 | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 15s 934ms | Passed |
| Admin Add Batch to OMS | 2 | 2 | 0 | 10 | 10 | 0 | 0 | 0 | 0 | 0 | 25s 604ms | Passed |
| Admin can check OMS all the Button on side bar of oms webpage | 1 | 1 | 0 | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 10s 538ms | Passed |
| Admin can check oms webpage in different size on web browser | 2 | 2 | 0 | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 26s 149ms | Passed |
| Open OMS Homepage | 1 | 1 | 0 | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 46s 353ms | Passed |
| Admin Add Track to OMS | 1 | 1 | 0 | 5 | 5 | 0 | 0 | 0 | 0 | 0 | 50s 399ms | Passed |
| Admin can check User button for OMS | 1 | 1 | 0 | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 10s 021ms | Passed |
| 8 | 10 | 10 | 0 | 45 | 45 | 0 | 0 | 0 | 0 | 0 | 4m 14s 609ms | Totals |


User's Guide to the XXX Editor
Tycho Home Page
Copyright © 1998, The Regents of the University of California. All rights reserved.
Last updated: %G%, comments to:
tycho@eecs.berkeley.edu


Name
Age
Email
Skin color
Are you a student?
Yes
No
Select subjects?
English
Mathematics
Physics
Chemistry
Biological data
Age
Show links


[viewBag] ==
Our imperfect financial system today has overlooked SMEs by making capital access a slow and difficult process.
We understand you are frustrated.
We are on a mission to develop proprietary technologies to change this. 井Cashtag is a platform that will connect businesses to fast, convenient and transparent financing.
The 井Cashtag team comprises of experienced professionals in both the financial (specifically lending) and technology industries, marrying the best of both worlds as we continuously develop groundbreaking products and services.


| Sub-Organization Name | Owner Name | Owner Email | |
|---|---|---|---|
| {$ sub_org.name $} |
{$ owner.first_name $} {$ owner.last_name $} |
{$ owner.email $} |
Remove |


log out
Security check
Incorrect numbers. You can repeat the attempt in 3 hours.
You are trying to access
...
's account from an unusual place.
In order to confirm ownership of the page, we ask you to enter all the
missing digits
of the phone number linked to your account.
Log out


A bit more about me....I like to BBQ and practice coding. Also baking. I hope to attend Hack Reactor to polish and expand my skills and portfolio! Contact me by Clicking Here
Questions? Concerns? Wanna hear more?. Please leave your feedback and which county you're from!
How many times do you eat out per month?


Tokyo Olympics 2020 Countdown
The next Olympic games will be held in Tokyo, Japan from July 24th to August 9th 2020.
RESULTS
DAYS
HOURS
MINUTES
SECONDS


It all started when a twenty-six year old woman named Mimi Vallée brought three individual entrepreneurs together. As close friends to Mimi in a gluten-intolerant advice and support group, these three were the first to witness her despair when she realized that as a single mother of two, not only was there no way she was able to spend the time to make gluten-free food like her three friends could, but also that there were no gluten-free restaurants near her.
As it happened to be, these three friends made contact with each other almost immediately. Having already been gluten-intolerant themselves, they were already wavering whether to do something about it. Mimi was the final push: with this event in their lives shaping their experiences, Alina Ying, Pratibha Sriram, and Ria Pradeep came together to start a gluten-free restaurant and bakery on July 1st, 2014.
Création was the name.
Creation is the act of making or producing something that did not exist before; the act of creating something. We saw the need, and we came to fulfill it. As a nod to our dear friend Mimi Vallée, we decided to make our restaurant centered around French cuisine.
On February 19th, 2015, we drew up a business plan with a clear definition of our concept, a description of our market, menu and pricing, as well as detailed financial information, including data on our startup capital and long-term income and expense forecasts.
On July 24th, 2015, the location of 3481 Melrose Place in Santana Row, California was finally officially settled. Our vision for the restaurant and bakery kept on growing bigger and bigger.
On December 2nd, 2015, we hired Head Chef Louis Jean-Benoït, Assistant Chef Pierre de la Rue, and patisserie Rose Anne-Thérèse, three clear leaders in their respective niches. We also found 7 dedicated waiters. Our journey was about to begin.
We opened our restaurant and bakery on January 1st, 2016. With the start of a new year comes the start of a new era.
We pledge...
To fulfill our customers’ needs.
To provide our food with integrity.
To become the pioneering leader of gluten-free restaurants and bakeries.
To donate 15% of our proceeds to celiac disease research.
To cherish each and every one of our customers.
To create something legendary.
Création has received acclaim in its own neighborhood and was awarded 1st place in the CFC Chili Cook-Out and received the Judges' Choice Award just one and a half months after its opening! It is now on proud display at our bakery as an important milestone to Création’s journey.
Having only started this year, Création is also immensely honored to have already received the Bib Gourmand Award recently. Not only does the award prove that Création has good food, it also shows that Création is affordable. To our restaurant’s goal and vision, there can be no higher compliment. We strive to provide the opportunity of good eating to gluten-intolerant customers, and help treat celiac disease as a whole.
Création is looking forward to further achievements, and will not stop until it has reached the pinnacle of gluten-free cuisine. As the proud owners of this restaurant and bakery, Alina Ying, Pratibha Sriram, and Ria Pradeep extend their thanks to all their customers and supporters. Thank you for helping us come so far, and we are looking forward to your future assistance.
We welcome all of you to join us on the rest of our journey!
"It may have taken me over twenty years, but I think I’ve found the best restaurant in the United States. It is a very, very rare occasion that I praise a single restaurant or bakery this highly, but I truly believe Création deserves this acclaim."
- Gord On Ramsay"The quality of food here is wonderful. Good food. Good eating. Good life."
- Linguini Montgomery"I have been on a strict, gluten-free diet since I was three years old. Thank you. For all the effort the owner, the head chefs, and the servers put in. It’s people like you guys who make the world a better place to live in."
- Brigette Evans


On the left Map dragging and worldCopyJump are enabled during initialization. On the right Map worldCopyJump is enabled. Dragging is enabled by clicking the button.


Introduction
Choirless is a musical collaboration platform built to enter the
Call for Code 2020 global competition.
It allows music groups to create a video wall recording of a piece of music, where all of the
individual submissions are captured separately on the performers' phone, tablet or laptop.
The core technology that powers Choirless has been released as a Call for Code with The Linux Foundation project named
Rend-o-matic.
Choirless came about during the 2020 Covid-19 Pandemic, as countries went into lockdown
and social-distancing prevented choirs, bands and other musical ensembles from meeting and
performing in person. Video meeting platforms such as Zoom and Webex proved useless for live
collaboration because of the network latency and the audio being optimised for speech.
Choirless aims to make it very easy for choir leaders to create songs made out of several parts
(e.g. alto, tenor, soprano) and to organise choir members to provide renditions of a part.
All of the contributed videos are stitched together into a video wall with no special
equipment and without employing costly and time-consuming video editing software.
Who are we?
The Choirless Crew consist of three members who met at IBM:
How does it work?
Choirless consists of a number of microservices working together:
Choirless components
- The Application Server powers the
Choirless front-end website. It is built with HTML and vanilla JavaScript and is deployed using
Cloud Foundry in the IBM Cloud
- The Choirless API is a RESTful API service
written in Node.js which stores its state in a Cloudant JSON document store.
Rend-o-matic components
- The Renderer is a collection of Python
and Node.js serverless actions that run in the IBM Cloud Functions framework. They are triggered
by objects arriving in Cloud Object Storage and from user-driven events. Each action performs a
specific audio/video processing job, such as resizing video or creating montages of multiple
video sources.
- Boxjam is a Node.js module that fits
many rectangles into another rectangle - useful if you're creating video montages of videos
that are of various sizes and orientations.
- Stitcheroo is a Node.js module that fits
many combines multiple videos sources into an output montage. It can also process the
audio tracks for stereo spread and reverberation ambience.
The Rendering Process
The Rendering service is a multi-step process, where each step is triggered by the arrival of an
object in a COS bucket. Some actions are taken in parallel to speed up the process:
How can I try it?
Simpy visit
Choirless.com and sign up for an account. Or, check
out the
Rend-o-matic GitHub organisation to explore the
underlying engine that powers Choirless.
Open source
All of our source code is
open-sourced under the MIT
license on GitHub.
© Choirless 2020-2021


HR Advisor Package
HR Department
HR Audit
Employee Handbook
Job Descriptions & Linked Performance Appraisals
Employee Hotline
Project or Onsite Consulting
Additional HR Services


Web Development is more than a career. It's a lifestyle, a community, and a boundless playground for creativity.
Collaborated with team to design and develop mobile app for National Veterans Transition Service site. Individually developed timeline component, the functional axis of the site. Worked closely with client to ensure progress tracking was easy to see, understand, and use. Responsible for UI, layout, design and content. Graphing, progress tracking, recursion, data model. Test and debug code. Assist in the facilitation of the MEAN Stack education program.
Developed and modified new website for IKFF with WordPress. Graphics work with Adobe Illustrator. Troubleshoot and maintain DNS server. Create new database of Certified Trainers. Integrated PayPal, Eventbrite API's. Improve SEO using Yoast SEO plugin. Simplify sale of merchandise through WooCommerce.
Reconciled legal and medical documents with submitted claims and additional evidence. Veriﬁed, reconciled, and issued payments to claimants, attorneys, and various state legislatures. Created functional Excel sheet to expedite calculation of beneﬁts due in compliance with CA state requirements.
Production-based data analysis and entry role. Accurately entered members and providers information, including CPT, diagnosis, POS, DOS, in standard HFCA 1500 and UB-04 claim forms. Familiarization of Medicaid, Medicare and Hospital contracts. Processed and adjudicated claims for analysts and editors. Prepared claims for payment, denial, or pending for further review. Veriﬁed all data before adjudication.
Although these roles varied in scope of work, keeping the customer happy was a key aspect of all. As an account manager, I communicated weekly with Clients to reconcile and manage payables for over 90 accounts. Prepared, reviewed, and prcessed expense reports. Varified that claims complied with state and local legislation to ensure validity, providing technical reports and information to and/or on behalf of the institution. Reviewed and maintained standing working orders/purchase orders, adhering to agreed costs and spending. Coordinated both department, and facility-wide events.
Monitored customer experience, and compliance performance at multiple branches. Provided solutions based on needs discovered during customer-facing time. Assisted customers with online and mobile applications usage. Created and implemented loss plan, returning branch to acceptable levels in one quarter. Trained transferring staff from Wachovia in multi-state merge program on system usage, operations and service standards, and account managment best practices.
Introduction to Data Science, using Python and its libraries.
MEAN Stack, Ionic PWA, CSS, HTML
Fudamentals of CA Worker's Compensation Claims Handling
Proud dog dad (Volt and Banjo), and the great outdoors - especially the water...it's my happy place. I'm a long-time camping enthusiast, and enjoy road trips (on or off-road) to get there. Hiking with my dogs has become my weekend treat to myself. I've become a plant nerd and have a growing (no pun intended!) collection of plants, mostly cactus and succulents. This year, I actively started incorporating flowering plants that attract hummingbirds, butterflies, or bees.
As for the great INdoors, I'm big foodie - cooking, eating, or even just talking about it...yum. And, what goes better after a great meal than a pile of dogs around you and something good on the tv?! I love a good laugh, anything with great CGI fight scenes - or a wicked car chase. I've been a gamer since the original NES - and you should know that I'm lethal at MarioKart. :)


Itemator
UX/UI, Mobile app, end-to-end design
Summary
Refining the process for receipt itemizations, processing and OCR in the Concur mobile app.
Receipt itemizations are one of the largest pain points for Concur power users.
Problem
Receipts, especially hotel bills are difficult to scan, which means users often have to manually input all individual expenses, from hotel lodging expenses to room service to small surcharges and fees.
The current user flow made it impossible to review itemizations before processing it to the cloud. The overall flow is slow, inaccurate and difficult to gather accurate data from. Most users, in their frustration, roll up all their expenses to save time.
Understanding the User
My research process included
- Whiteboarding user journey maps and identified pain points
- Interviewing users to describe their own experience through various steps
- Collaborating with data scientists to understand their own issues with the current flow
- Collecting user data on usage times, highest pain points & priorities
After research comes iteration
After fully mapping out the new flows and finishing user research I worked on,
- Mapping the full user journey (Below)
- Sketching new user flows and screen flows (Left)
- Communicating with multiple teams and stakeholders throughout various iterations and steps of the project
- Designing the final UI, based off the current Concur Mobile design library
I worked on this design from beginning to end, overseeing the entire process.
Outcome
The project started out as just hotel itemizations, but it eventually tackled an even broader issue: scanning & processing all receipts, more accurately and quickly.
By using more accurate OCR and continuously scanning multiple frames of a receipt, the confidence level* improved from 20~ to 80%.
Although the process still isn't perfect in detecting finer details such as vendor, date, city, it's a large improvement in terms of accuracy, speed and user experience.
Improvements include
- Smoother user flow
- Reduced processing time from ~10 minutes to under 30 seconds
- OCR scanning confidence quadrupled
- Easier to itemize, which means more usable data for user research and further improvements
Initial user testing came back with positive user feedback, users were especially happy with the faster processing times, as well as less time spent manually inputting their receipts. The data scientist team received more usable OCR data, thanks to more accurate scanning and input of expenses.
*Level of confidence reading the information on a receipt
Next steps
The app was for demoed for keynotes at Concur's annual conferences, including Fusion North America and Fusion EMEA. Further development has been made and the features has been put into production on the Concur beta mobile platform.