Hi oy lf
This is a great question, thanks!
Since I don't have your specific underlying dataset, I'm going to instead use the PDL1 data in your attachment, which a quick Google informs me is taken from the Supplementary Information (Figure S1) of Liu X et al. Int. J. Cancer 2020; 147: 116–127. doi:10.1002/ijc.32744
The data can be entered into Stata as follows:
We begin by processing the data into a form suitable for meta-analysis; note that the first encode command is simply so that we maintain the same order of studies as in Figure S1 (otherwise they would be re-sorted alphabetically):
Now, Figure S1 effectively shows two separate meta-analyses, but presents them as interlaced. So to recreate the figure, we will do the same thing: fit both analyses separately, take the results, and re-sort them so that the PD-L1 positive and negative results alternate.
Finally, we need to do a bit of tidying up before we create the final plot:
The following line of code will now almost recreate Figure S1:
The only thing missing is the second vertical line (the orange line corresponding to PD-L1 negative). Unfortunately, there is currently a bug in the forestplot command which prevents this from displaying. We would like to add the option dataid(PDL1), but as you would see if you tried it, it gives an error message. I have corrected the error in a beta version, and can confirm that the line does appear once the bug is fixed (see attachment). I shall release a new version of the metan package as soon as possible -- sorry about that!
I hope that this gives you some useful pointers.
Best wishes,
David.
This is a great question, thanks!
Since I don't have your specific underlying dataset, I'm going to instead use the PDL1 data in your attachment, which a quick Google informs me is taken from the Supplementary Information (Figure S1) of Liu X et al. Int. J. Cancer 2020; 147: 116–127. doi:10.1002/ijc.32744
The data can be entered into Stata as follows:
Code:
* Example generated by -dataex-. For more info, type help dataex clear input str26 Disease str23 StudyName str25 Intervention str11 Control int(IntN ControlN) float(HR_pos HRlci_pos HRuci_pos HR_neg HRlci_neg HRuci_neg) "Non-small-cell lung cancer" "CheckMate 057, 2017" "Nivolumab" "Docetaxel" 231 224 .43 .3 .63 1.01 .76 1.33 "Non-small-cell lung cancer" "CheckMate 017, 2017" "Nivolumab" "Docetaxel" 117 108 .53 .31 .89 .7 .47 1.02 "Non-small-cell lung cancer" "POPLAR, 2016" "Atezolizumab" "Docetaxel" 144 143 .54 .33 .89 .84 .53 1.32 "Non-small-cell lung cancer" "OAK, 2016" "Atezolizumab" "Docetaxel" 607 608 .64 .49 .84 .85 .73 1 "Melanoma" "CheckMate 067, 2018 (a)" "Nivolumab" "Ipilimumab" 288 277 .65 .42 .99 .64 .5 .82 "Melanoma" "CheckMate 067, 2018 (b)" "Nivolumab plus ipilimumab" "Ipilimumab" 278 277 .56 .35 .9 .54 .42 .69 "Melanoma" "CheckMate 069, 2016" "Nivolumab plus ipilimumab" "Ipilimumab" 35 83 .78 .26 2.4 .74 .38 1.47 "Melanoma" "CheckMate 066, 2018" "Nivolumab" "Dacarbazine" 120 243 .16 .07 .38 .56 .37 .85 "Melanoma" "CheckMate 037, 2017" "Nivolumab" "CTx" 201 195 .73 .49 1.09 1.15 .82 1.62 "Head and neck cancer" "CheckMate 141, 2018" "Nivolumab" "SOC" 172 103 .5 .3 .83 .81 .55 1.21 end
We begin by processing the data into a form suitable for meta-analysis; note that the first encode command is simply so that we maintain the same order of studies as in Figure S1 (otherwise they would be re-sorted alphabetically):
Code:
label define StudyName_ 1 "CheckMate 057, 2017" label define StudyName_ 2 "CheckMate 017, 2017", add label define StudyName_ 3 "POPLAR, 2016", add label define StudyName_ 4 "OAK, 2016", add label define StudyName_ 5 "CheckMate 067, 2018 (a)", add label define StudyName_ 6 "CheckMate 067, 2018 (b)", add label define StudyName_ 7 "CheckMate 069, 2016", add label define StudyName_ 8 "CheckMate 066, 2018", add label define StudyName_ 9 "CheckMate 037, 2017", add label define StudyName_ 10 "CheckMate 141, 2018", add rename StudyName StudyNameStr encode StudyNameStr, gen(StudyName) label(StudyName_) order StudyName, after(StudyNameStr) drop StudyNameStr rename (HR_pos HRlci_pos HRuci_pos) (HR1 HR1_lci HR1_uci) rename (HR_neg HRlci_neg HRuci_neg) (HR2 HR2_lci HR2_uci) reshape long HR@ HR@_lci HR@_uci, i(StudyName) j(PDL1) gen double lnHR = ln(HR) gen double lnHR_lci = ln(HR_lci) gen double lnHR_uci = ln(HR_uci)
Now, Figure S1 effectively shows two separate meta-analyses, but presents them as interlaced. So to recreate the figure, we will do the same thing: fit both analyses separately, take the results, and re-sort them so that the PD-L1 positive and negative results alternate.
Code:
preserve tempfile neg metan lnHR lnHR_lci lnHR_uci if PDL1==2, random study(StudyName) by(Disease) lcols(Intervention Control IntN ControlN) nosubgroup nograph clear save `neg' restore metan lnHR lnHR_lci lnHR_uci if PDL1==1, random study(StudyName) by(Disease) lcols(Intervention Control IntN ControlN) nosubgroup nograph clear append using `neg', gen(PDL1) isid _BY _USE _STUDY PDL1, sort miss
Finally, we need to do a bit of tidying up before we create the final plot:
Code:
replace PDL1 = PDL1 + 1 label define PDL1_ 1 "PD-L1 Positive" 2 "PD-L1 Negative" label values PDL1 PDL1_ drop if inlist(_USE, 0, 6) & PDL1==1 replace PDL1 = . if inlist(_USE, 0, 6) replace _LABELS = "Pooled estimate in PD-L1 positive at 5% cut-off" if _USE==5 & PDL1==1 replace _LABELS = "Pooled estimate in PD-L1 negative at 5% cut-off" if _USE==5 & PDL1==2 replace _LABELS = "" if _USE!=5 & PDL1==2 label variable _LABELS "Study name" format %-1s _LABELS gen IntString = Intervention + " (n=" + strofreal(IntN, "%3.0f") + ")" if !missing(IntN) & PDL1==1 label variable IntString `""Intervention " "(No. of patients analysed)""' format %-1s IntString gen ControlString = Control + " (n=" + strofreal(ControlN, "%3.0f") + ")" if !missing(ControlN) & PDL1==1 label variable ControlString `""Control" "(No. of patients analysed)""' format %-1s ControlString
The following line of code will now almost recreate Figure S1:
Code:
forestplot, hr nowt plotid(PDL1) lcols(IntString ControlString) xlabel(.5 "0.5" 1 2) range(.0625 2.5) cirange(.08 2.5) /// astext(70) textsize(90) boxsca(80) favours(Favours intervention # Favours control, fp(3)) /// box1opts(mcolor(edkblue)) box2opts(mcolor(orange)) ciopts(lcolor(black) lw(thin)) /// diam1opts(lc(edkblue)) diam2opts(lc(orange)) oline1opts(lp(dash) lc(edkblue)) oline2opts(lp(dash) lc(orange)) graphregion(color(white))
I hope that this gives you some useful pointers.
Best wishes,
David.
Comment