*! aevolcano version 1.0 18Dec2019
help aevolcano

Title

aevolcano -- Volcano plot for adverse event data

Syntax

aevolcano varname , treat(varname) id(varname) n1(integer) n2(integer) [options]

aevolcano requires data in long format with one row per event per participant, where varname indicates the variable that contains the event name/identifier.
varname may be a numeric or a string variable.

* treat(varname) variable indicating treatment group assignment in the existing dataset (must be numeric)

* id(varname) variable identifying unique participants in the existing dataset, multiple events (rows) per id acceptable (must be numeric)

* n1(#) the total number of unique participants in the first treatment group (must be an integer value)

* n2(#) the total number of unique participants in the second treatment group (must be an integer value)

saving(filename[, replace]) saves the dataset with the plotted event level summary data in filename.dta

graphsave(filename[, replace]) saves the plot in filename.dta

clear if specified the newly created dataset is stored in memory. If clear not specified the original dataset is retained in memory.

oddsratio(#) option to plot odds ratios; oddsratio(1) plots the odds ratio and oddsratio(0) plots the risk difference, default is oddsratio(0)

riskratio(#) option to plot risk ratios; riskratio(1) plots the risk ratio and riskratio(0) plots the risk difference, default is riskratio(0)

pvalue(#) option to use p-values from Pearson's chi-squared test; pvalue(1) uses Pearson's chi-squared p-values and pvalue(0) uses Fisher's exact p-values, default is pvalue(0)

padj(#) option to use the false discovery rate (FDR) p-value adjustment; padj(1) produces FDR adjusted p-values and padj(0) uses no adjustment, default is padj(0). See aefdr for full details

* fdrhigher(varname) if padj(1) then fdrhigher(varname) required, where varname indicates the higher level or bodysystem event variable

* fdrlower(varname) if padj(1) then fdrlower(varname) required, where varname indicates the lower level or preferred term event variable

fdrval(#) indicates the alpha value the FDR adjustment is carried out on. The FDR procedure flags events with adjusted event and bodysystem p-values below this specified value (must be numeric), default is fdrval(0.1)

labelyn(#) used to turn bubble labels on by specifying labelyn(1); default is bubble labels off labelyn(0)

label(#) if labelyn(1) then label used to indicate p-value threshold below which bubbles will be labelled; default is label(1) so variables with log10(p-value)>1 will be labelled,
this equates to a p-value<0.1

labelnum(#) used to specify if bubble labels required when varname numeric; default is labelnum(0) which indicates number labels not required,
labelnum(1) labels assigned numeric values, labelnum(2) labels assigned label values

labcol(colorstyle) label text colour; default is labcol(black)

labcol1(colorstyle) label text colour for events where the risk is largest in the first treatment group; default labcol1(black)

labcol2(colorstyle) label text colour for events where the risk is largest in the second treatment group; default labcol2(black)

labang(#) label angle; default labang(90) to give vertical labels

labang1(#) label angle for events where the risk is largest in the first treatment group; default labang1(90) to give vertical labels

labang2(#) label angle for events where the risk is largest in the second treatment group; default labang2(90) to give vertical labels

labpos(#) label position; default labpos(12) to give labels above the bubble

labpos1(#) label position for events where the risk is largest in the first treatment group; default labpos1(12) to give labels above the bubble

labpos2(#) label position for events where the risk is largest in the second treatment group; default labpos2(12) to give labels above the bubble

labgap(#) gap between label and bubble; default labgap(5)

labgap1(#) gap between label and bubble for events where the risk is largest in the first treatment group; default labgap1(5)

labgap2(#) gap between label and bubble for events where the risk is largest in the second treatment group; default labgap2(5)

xaxismin(#) allows the user to extend the x-axis beyond the minimum plotted value; default is 0 so minimum value used

xaxismax(#) allows the user to extend the x-axis beyond the maximum plotted value; default is 0 so maximum value used

yaxismin(#) allows the user to extend the y-axis beyond the minimum value; default is 0 so minimum value used

yaxismax(#) allows the user to extend the y-axis beyond the maximum value; default is 0 so maximum value used

xaxisticks(#) allows the user to specify how the x-axis ticks are spaced; default is 4

yaxisticks(#) allows the user to specify how the y-axis ticks are spaced; default is 4

xaxisdp(#) allows the user to specify the unit x-axis values are rounded to; default is 0.1

yaxisdp(#) allows the user to specify the unit y-axis values are rounded to; default is 0.1

ylineyn(#) allows the user to specify if a horizontal line is plotted by specifying ylineyn(1); default is ylineyn(0) which does not plot a line

yline(numlist) allows the user to specify the y-axis value where the horizontal line is plotted if ylineyn(1); default is yline(1) which equates to a p-value of 0.1, unless padj(1)

ylinepat(linepattern) style of yline(); default is ylinepat(dash)

ylinecol(colorstyle) colour of yline(); default is ylinecol(bluishgrey)

ylinewidth(#) width of yline(); default is ylinewidth(0.5)

mfcolor1(colorstyle) bubble fill colour for events where the risk is largest in the first treatment group; default is mfcolor1(red)

mfcolor2(colorstyle) bubble fill colour for events where the risk is largest in the second treatment group; default is mfcolor2(blue)

mfcolor(colorstyle) overall bubble fill colour; default is to assign unique colours dependent on direction of effect, mfcolor1(red) and mfcolor2(blue)

mlcolor1(colorstyle) bubble outline colour for events where the risk is largest in the first treatment group; default is mlcolor1(red)

mlcolor2(colorstyle) bubble outline colour for events where the risk is largest in the second treatment group; default is mlcolor2(blue)

mlcolor(colorstyle) overall bubble outline colour; default is to assign unique colours dependent on direction of effect, mlcolor1(red) and mlcolor2(blue)

legendyn(#) specify whether legend appears on the plot; legendyn(#) takes value 0 to indicate legend off or 1 to indicate legend on, default is legendyn(0)

legend1(string) specify text to describe the first treatment group; if legendyn(1) and legend1() default text is "Risk in Group 1" when the risk difference or the risk ratio is plotted
or "Odds higher in Group 1" when the odds ratio is plotted

legend2(string) specify text to describe the second treatment group; if legendyn(1) and legend2() default text is "Risk in Group 2" when the risk difference or the risk ratio is plotted
or "Odds higher in Group 2" when the odds ratio is plotted

legendpos(#) legend position; if legendyn(1) default position is legendpos(6)

legendcol(#) number of columns in legend; if legendyn(1) default is legendcol(1)

legendrow(#) number of rows in legend; if legendyn(1) default is legendrow(2)

grphcol(colorstyle) colour of the graph background; default is grphcol(white)

plotcol(colorstyle) colour of the plot background; default is plotcol(white)

* id(), treat(), n1 and n2 are
required.

* if padj(1) then fdrhigher() and fdrlower() are
required.

Description

aevolcano creates a volcano plot for adverse event data from a two-arm clinical trial,
as proposed by Zink, Wolfinger, and Mann (2013).
The volcano plot is a means of displaying the incidence of multiple adverse events simultaneously.
Each bubble represents an event, the size of which indicates total frequency of that event,
colour is used to indicate direction of treatment effect and colour saturation corresponds
to the statistical significance of the treatment effect.
The volcano plot can help to identify potential differences in the adverse event profile between treatment arms.

Options

treat(varname) specifies the variable identifying treatment group assignment in the
existing dataset. treat() is required and must be a numeric variable. The command will always
process lowest coded value first.

id(varname) specifies the variable identifying unique participants in the
existing dataset. id() is required and must be a numeric variable.
Multiple events (rows) per id is acceptable.

n1(#) specifies the total number of unique participants in the first treatment group.
n1() is required and must be a numeric variable.

n2(#) specifies the total number of unique participants in the second treatment group.
n2() is required and must be a numeric variable.

saving(filename[, replace]) saves the dataset
with the newly generated event level summary data. This data is used to produce the plot.
A new filename is required unless replace is also specified.
replace allows the filename to be overwritten with new data.

graphsave(filename[, replace]) saves the plot.
A new filename is required unless replace is also specified.
replace allows the filename to be overwritten with a new plot.

clear if specified the newly created dataset is stored in memory.
If clear not specified the original dataset is retained in memory.

oddsratio(#) used to specify whether odds ratios are plotted instead of risk differences;
oddsratio(0) plots risk differences and oddsratio(1) plots odds ratios.
oddsratio(#) can only take values 0 or 1. The default is oddsratio(0).
Only one of oddsratio(#) or riskratio(#) can be specified at a time.

riskratio(#) used to specify whether risk ratios are plotted instead of risk differences;
riskratio(0) plots risk differences and riskratio(1) plots risk ratios.
riskratio(#) can only take values 0 or 1. The default is riskratio(0).
Only one of oddsratio(#) or riskratio(#) can be specified at a time.

pvalue(#) used to specify which test is used to calculate the p-value.
pvalue(1) calculates and plots Pearson's chi-squared p-values and pvalue(0) calculates and plots Fisher's exact p-values.
The default is pvalue(0).

padj(#) used to specify whether the false discovery rate (FDR) p-value adjustment as proposed by
Mehrotra and Adewale (2012) is used to flag events. padj(1) calculates the FDR adjusted p-values and
padj(0) does not calculate adjusted values. The default is padj(0). padj(1) calls on aefdr which creates a new dataset,
adjusted_pvalues.dta containing summary level data including an adjusted p-value for each fdrlower event and fdrhigher event.
The new dataset, adjusted_pvalues.dta is saved to the current working directory. See aefdr for full details.

fdrhigher(varname) is required if padj(1).
varname is the variable that contains the higher level or bodysystem event terms. See aefdr for full details.

fdrlower(varname) is required if padj(1).
varname is the variable that contains the lower level or preferred term event names. See aefdr for full details.

fdrval(#) indicates the alpha value that the FDR adjustment is carried out on.
Events with adjusted event (lower level) and bodysystem (higher level) p-values below this specified value are flagged.
Default value is 0.1. See aefdr for full details.

labelyn(#) used to turn bubble labels on by specifying labelyn(1).
Default is bubble labels off labelyn(0). labelyn(#) can only take values 0 or 1.
If labelyn(1) labels are on and variables with log10(p-value)>1 will be labelled as default.
Note if no variables have log10(p-value)>1 then no labels will appear even if labelyn(1).

label(#) if labelyn(1) then label used to indicate the value of p-values below which to label bubbles.
Default is label(1) which labels bubbles with log10(p-values)>1, which equates to
labelling bubbles with a p-value<0.1.

labelnum(#) used to specify if labels are required when varname is numeric.
Default is labelnum(0) which indicates number labels not required,
labelnum(1) used to indicate labels assigned numeric values, and
labelnum(2) indicates labels assigned label values. labelnum(#) can only take values 0, 1 or 2.

labcol(colorstyle) specifies an overall colour for label text if labelyn(1).
labcol() must be one of Stata's colorstyle; default is labcol(black).

labcol1(colorstyle) specifies label text colour when the risk (RD, OR or RR) is largest
in the first treatment group when labelyn(1). labcol1() must be one of Stata's colorstyle; default labcol1(black).
labcol1() and labcol2() can be used to differentiate the colour of bubble labels according to the direction
of the treatment effect.

labcol2(colorstyle) specifies label text colour when the risk (RD, OR or RR) is largest
in the second treatment group when labelyn(1). labcol2() must be one of Stata's colorstyle; default labcol2(black).
labcol1() and labcol2() can be used to differentiate the colour of bubble labels according to the direction
of the treatment effect.

labang(#) specifies the angle for labels. The default is labang(90) to give vertical labels.
labang(#) takes integer values only.

labang1(#) specifies the angle for labels when the risk (RD, OR or RR) is largest in the first treatment group.
The default is labang1(90) to give vertical labels. labang1(#) takes integer values only.
labang1(#) and labang2(#) can be used to differentiate the angle of the labels according to the direction
of the treatment effect.

labang2(#) specifies the angle for labels when the risk (RD, OR or RR) is largest in the second treatment group.
The default is labang2(90) to give vertical labels. labang2(#) takes integer values only.
labang1(#) and labang2(#) can be used to differentiate the angle of the labels according to the direction
of the treatment effect.

labpos(#) specifies the label position. The default is labpos(12)
which positions labels above the bubble. labpos(#) can only take integer values between 0 and 12, inclusive.

labpos1(#) specifies the label position when the risk (RD, OR or RR) is largest in the first treatment group.
The default is labpos1(12) which positions labels above the bubble.
labpos1(#) and labpos2(#) can be used to differentiate the position of the bubble labels according to the direction
of the treatment effect. labpos1(#) can only take integer values between 0 and 12, inclusive.

labpos2(#) specifies the label position when the risk (RD, OR or RR) is largest in the second treatment group.
The default is labpos2(12) which positions labels above the bubble.
labpos1(#) and labpos2(#) can be used to differentiate the position of the bubble labels according to the direction
of the treatment effect. labpos2(#) can only take integer values between 0 and 12, inclusive.

labgap(#) specifies the size of the gap between the label and the bubble.
The default gap is labgap(5). labgap(#) takes integer values only.

labgap1(#) specifies the size of the gap between the label and the bubble when the risk (RD, OR or RR) is largest in the first treatment group.
The default gap is labgap1(5). labgap1(#) takes integer values only.
labgap1(#) and labgap2(#) can be used to differentiate the
size of the gap between the labels and bubble according to the direction of the treatment effect.

labgap2(#) specifies the size of the gap between the label and the bubble when the risk (RD, OR, or RR) is largest in the second treatment group.
The default gap is labgap2(5). labgap2(#) takes integer values only.
labgap1(#) and labgap2(#) can be used to differentiate the
size of the gap between the labels and bubble according to the direction of the treatment effect.

xaxismin(#) allows the user to add a value to the minimum value plotted to extend the x-axis.
The default is 0 which plots the minimum value for the x-axis lower limit.
Note that the # specified is added to the minimum value plotted to extend the x-axis.
The # specified is not the lower limit for the x-axis.

xaxismax(#) allows the user to add a value to the maximum value plotted to extend the x-axis.
The default is 0 which plots the maximum value for the x-axis upper limit.
Note that the # specified is added to the maximum value plotted to extend the x-axis.
The # specified is not the upper limit for the x-axis.

yaxismin(#) allows the user to add a value to the minimum value plotted to extend the y-axis.
The default is 0 which plots the minimum value for the y-axis lower limit.
Note that the # specified is added to the minimum value plotted to extend the y-axis.
The # specified is not the lower limit for the y-axis.

yaxismax(#) allows the user to add a value to the maximum value plotted to extend the y-axis.
The default is 0 which plots the maximum value for the y-axis upper limit.
Note that the # specified is added to the maximum value plotted to extend the y-axis.
The # specified is not the upper limit for the y-axis.

xaxisticks(#) allows the user to specify how the x-axis ticks are spaced by
dividing the range of the x-axis values by xaxisticks(#).
The default is 4.

yaxisticks(#) allows the user to specify how the y-axis ticks are spaced by
dividing the range of the y-axis values by yaxisticks(#). The default is 4.

xaxisdp(#) allows the user to specify the units the x-axis values are rounded to.
The default is 0.1 which rounds to 1 decimal place.

yaxisdp(#) allows the user to specify the units the y-axis values are rounded to.
The default is 0.1 which rounds to 1 decimal place.

ylineyn(#) allows the user to specify if a horizontal line is included on the plot.
The default is ylineyn(0) which does not include a line on the plot.

yline(numlist) can be specified if ylineyn(1). It allows the user to specify the y-line value for the horizontal line.
The default is yline(1) which equates to a p-value of 0.1, unless padj(1) then yline(#) is plotted at the minimum adjusted p-value flagged by
the aefdr procedure and if no events flagged after adjustment the line is set at yline(0).

ylinepat(linepattern) used to specify the style of yline().
ylinepat() must be one of Stata's linepattern; the default is ylinepat(dash).

ylinecol(colorstyle) used to specify the colour of yline().
ylinecol() must be one of Stata's colorstyle; default is ylinecol(bluishgray).

ylinewidth(#) specifies the width of yline(). The default is ylinewidth(0.5).

mfcolor1(colorstyle) specifies the bubble fill colour for events where the risk is largest in the first treatment group.
mfcolor1() must be one of Stata's colorstyle, the default is mfcolor1(red).

mfcolor2(colorstyle) specifies the bubble fill colour for events where the risk is largest in the second treatment group.
mfcolor2() must be one of Stata's colorstyle, the default is mfcolor2(blue).

mfcolor(colorstyle) specifies one colour for all bubbles.
Default is to assign unique treatment group colours using mfcolor1(red) and mfcolor2(blue).

m1color1(colorstyle) specifies the bubble outline colour for events where the risk is largest in the first treatment group.
mlcolor1() must be one of Stata's colorstyle, the default is mlcolor1(red).

mlcolor2(colorstyle) specifies the bubble outline colour for events where the risk is largest in the second treatment group.
mlcolor2() must be one of Stata's colorstyle, the default is mlcolor2(blue).

mlcolor(colorstyle) specifies one colour for all bubble outlines.
Default is to assign unique treatment group colours using mlcolor1(red) and mlcolor2(blue).

legendyn(#) specifies whether a legend appears on the plot.
legendyn(#) takes values 0 to indicate legend off or 1 to indicate legend on.
The default is legendyn(0) so no legend included on the plot. legendyn(#) can only take values 0 or 1.

legend1(string) allows the user to specify the text to describe the first treatment group.
If legendyn(1) and legend1() then the default text is "Risk in Group 1" when the risk difference or the risk ratio is plotted
or "Odds higher in Group 1" when the odds ratio is plotted.

legend2(string) allows the user to specify the text to describe the second treatment group.
If legendyn(1) and legend2() then the default text is "Risk in Group 2" when the risk difference or the risk ratio is plotted
or "Odds higher in Group 2" when the odds ratio is plotted.

legendpos(#) specifies the position of the legend.
If legendyn(1) then the default position is legendpos(6).
legendpos(#) can only take integer values between 0 and 12, inclusive.

legendcol(#) specifies the number of columns in the legend.
If legendyn(1) the default is legendcol(1).
legendcol(#) can only take integer values.
Use with legendrow(#) to change legend appearance.

legendrow(#) specifies the number of rows in the legend.
If legendyn(1) default is legendrow(2).
legendrow(#) can only take integer values.
Use with legendcol(#) to change legend appearance.

grphcol(colorstyle) specifies the colour of the graph background.
grphcol() must be one of Stata's colorstyle; the default is grphcol(white).

plotcol(colorstyle) specifies the colour of the plot background.
plotcol() must be one of Stata's colorstyle; default is plotcol(white).

Remarks

General remarks

General remarks

(1) Data are required in long format with one row per event per participant.
Participants can experience multiple different events and hence appear across multiple rows.

(2) The command creates a new dataset with one row per event containing summary level data.
Once the command finishes running the new dataset is stored in memory if clear is specified and is
saved in filename.dta if saving() is specified. If clear is
not specified the original dataset is kept in memory.

(3) Graphs are saved to the users current working directory if graphsave() is specified.

(4) treat(varname) must be a numerical variable. The command will always
process lowest coded value first. For example:

treat(1) - treat(2) or treat(0) - treat(1) and treat(1)/treat(2) or
treat(0)/treat(1)

(5) If more than two treatment arms then users are recommended to present separate graphs for each pairwise comparison.

(6) Users can change label position, angle and gap from bubble but occasionally labels will still overlap with each other,
in this scenario we advise users to switch labels off and insert labels manually or alternatively use a numeric variable and label with numbers.

(7) Labels are specified by choosing a log10(p-value) that should be exceeded e.g. log10(p-value)=1 equates to labelling events with p-values less than 0.1.

(8) The command incorporates a correction (adds 0.5 events to each group) when fitting odds ratios or risk ratios if there are 0 events in one of the treatment groups.

(9) Odds ratios and risk ratios are plotted on the log scale to ensure symmetrical x-axis.

(10) If the padj option used then a new dataset, adjusted_pvalues.dta with one row per event containing summary level data is saved to current working directory.
adjusted_pvalues.dta includes a variable, p2 containing the adjusted p-value for each event in fdrlower;
a variable, p2_bs containing the adjusted p-value for each event in fdrhigher; and a variable,
flag which equals 1 if events satisfy the p-value threshold specified in fdrval.

(11) If when running the program user receives error message "postfile ae_volc already exists" user needs to type postclose ae_volc before rerunning the command.

Examples

Analysing an example dataset

. use example_volcano.dta

Volcano plot

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31)

Volcano plot using the ylineyn option to include a horizontal line at the default value and using the yaxismax option to extend the y-axis

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) ylineyn(1) yaxismax(0.2)

Volcano plot using the ylineyn option to include a horizontal line and the yline option to specify at which value(s) the line appears

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) ylineyn(1) yaxismax(0.2) yline(1 2)

Volcano plot using the labelyn option to include labels on the plot

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) yaxismax(0.2) labelyn(1)

Volcano plot using the labelyn option to include labels on the plot and label to change p-value threshold at which labels appear

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) labelyn(1) yaxismax(0.2) label(0.5)

Volcano plot using the labelyn option to include labels on the plot and labcol to change the colour of the labels

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) labelyn(1) yaxismax(0.2) label(0.5) labcol(red)

Volcano plot using the labelyn option to include labels on the plot and labcol1 and labcol2 to change the
colour of the labels according to direction of the treatment effect

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) labelyn(1) yaxismax(0.2) label(0.5) labcol1(green) labcol2(red)

Volcano plot using the mfcolor1 and mfcolor2 options to change the colours of the bubbles
according to direction of treatment effect

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) mfcolor1(orange) mfcolor2(green)

Volcano plot using the legendyn option to include a legend on the plot

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) legendyn(1)

Volcano plot using the legendyn option to include a legend and legend1 and legend2 to change the legend labels

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) legendyn(1) legend1(Intervention) legend2(Placebo)

Volcano plot using the oddsratio option to plot the odds-ratio on the x-axis instead of the default risk-difference

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) oddsratio(1)

Volcano plot using the riskratio option to plot the risk-ratio on the x-axis instead of the default risk-difference

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) riskratio(1)

Volcano plot using the padj option to calculate the FDR adjusted p-values

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) padj(1) fdrlower(ae_pt) fdrhigher(aebodsys)

Volcano plot using the padj option to calculate the FDR adjusted p-values and fdrval to change the value at which events
are flagged

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) padj(1) fdrlower(ae_pt) fdrhigher(aebodsys) fdrval(0.05)

Saving the dataset used to produce the plot with filename volcano_example.dta

. use example_volcano.dta, clear

. aevolcano aebodsys, treat(arm) id(usubjid) n1(30) n2(31) saving(volcano_example)

Acknowledgments

We thank Emily Day (Imperial College London), Jack Elkes (Imperial College London) and
Giles Partington (Imperial College London) for their helpful comments on the program.

References

Zink, R.C. , Wolfinger, R.D. and Mann, G. 2013. Summarizing the incidence of adverse events using volcano plots and time intervals.
Clinical Trials 10: 398-406. doi:10.1177/1740774513485311

Mehrotra, D. V. and A. J. Adewale. 2012. Flagging clinical adverse experiences: Reducing false discoveries without materially compromising power for detecting true signals.
Statistics in Medicine 31: 1918-1930. doi:10.1002/sim.5310

Authors

Rachel Phillips

Imperial College London, UK

r.phillips@imperial.ac.uk

Suzie Cro

Imperial College London, UK