我正在尝试在绘图上添加分组线,其中值是从另一个数据帧计算的。
我的数据如下:
demo_df <- structure(list(Sample_Run = structure(c(3L, 4L, 2L, 1L, 5L, 3L,
1L, 4L, 5L, 2L, 3L, 2L, 1L, 5L, 4L), .Label = c("Sample1", "Sample2",
"Sample3", "Sample4", "Sample5", "Sample6", "Sample7", "Sample8",
"Sample9", "Sample10", "Sample11", "Sample12", "Sample13", "Sample14",
"Sample15", "Sample16", "Sample17", "Sample18", "Sample19", "Sample20"
), class = "factor"), MUT_ID = structure(c(1L, 1L, 1L, 1L, 1L,
3L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 2L), .Label = c("AKT1 c.49G>A",
"APC c.4348C>T", "APC c.4666_4667insA", "ATM c.1058_1059delGT",
"BRAF c.1799T>A", "CTNNB1 c.121A>G", "EGFR c.2236_2250del15",
"EGFR c.2310_2311insGGT", "EGFR c.2369C>T", "EGFR c.2573T>G",
"ERBB2 c.2324_2325ins12", "FGFR3 c.746C>G", "FLT3 c.2503G>T",
"GNA11 c.626A>T", "GNAQ c.626A>C", "GNAS c.601C>T", "JAK2 c.1849G>T",
"KIT c.2447A>T", "KRAS c.35G>A", "MPL c.1544G>T", "NPM1 c.863_864insTCTG",
"NRAS c.182A>G", "PDGFRA c.1694_1695insA", "PDGFRA c.2525A>T",
"PIK3CA c.1633G>A", "PIK3CA c.3140A>G", "PIK3CA c.3204_3205insA",
"PTEN c.741_742insA", "PTEN c.800delA", "RET c.2753T>C", "SMAD4 c.1394_1395insT",
"TP53 c.524G>A", "TP53 c.723delC", "TP53 c.743G>A", "TP53 c.818G>A"
), class = "factor"), FREQ = c(0.091, 0.077, 0.09, 0.096, 0.114,
0.081, 0.071, 0.076, 0.084, 0.083, 0.08, 0.082, 0.087, 0.085,
0.094)), .Names = c("Sample_Run", "MUT_ID", "FREQ"), row.names = c(1L,
4L, 5L, 7L, 8L, 46L, 47L, 48L, 50L, 51L, 91L, 93L, 94L, 96L,
97L), class = "data.frame")
demo_sd <- aggregate(demo_df[["FREQ"]], by=list(as.factor(demo_df[["MUT_ID"]])), FUN=sd)
names(demo_sd) <- c("MUT_ID", "sd")
demo_mean <- aggregate(demo_df[["FREQ"]], by=list(as.factor(demo_df[["MUT_ID"]])), FUN=mean)
names(demo_mean) <- c("MUT_ID", "mean")
demo_mean_sd <- merge(x = demo_sd, y = demo_mean)
demo_mean_sd[["sd_interval_upper"]] <- demo_mean_sd[["mean"]] + (2 * demo_mean_sd[["sd"]])
demo_mean_sd[["sd_interval_lower"]] <- demo_mean_sd[["mean"]] - (2 * demo_mean_sd[["sd"]])
输出:
> demo_df
Sample_Run MUT_ID FREQ
1 Sample3 AKT1 c.49G>A 0.091
4 Sample4 AKT1 c.49G>A 0.077
5 Sample2 AKT1 c.49G>A 0.090
7 Sample1 AKT1 c.49G>A 0.096
8 Sample5 AKT1 c.49G>A 0.114
46 Sample3 APC c.4666_4667insA 0.081
47 Sample1 APC c.4666_4667insA 0.071
48 Sample4 APC c.4666_4667insA 0.076
50 Sample5 APC c.4666_4667insA 0.084
51 Sample2 APC c.4666_4667insA 0.083
91 Sample3 APC c.4348C>T 0.080
93 Sample2 APC c.4348C>T 0.082
94 Sample1 APC c.4348C>T 0.087
96 Sample5 APC c.4348C>T 0.085
97 Sample4 APC c.4348C>T 0.094
> demo_mean_sd
MUT_ID sd mean sd_interval_upper sd_interval_lower
1 AKT1 c.49G>A 0.013390295 0.0936 0.12038059 0.06681941
2 APC c.4348C>T 0.005412947 0.0856 0.09642589 0.07477411
3 APC c.4666_4667insA 0.005431390 0.0790 0.08986278 0.06813722
我可以像这样制作基本情节:
library("ggplot2")
ggplot(data = demo_df,
aes(y = FREQ, x = Sample_Run, color = MUT_ID, group = MUT_ID) ) +
geom_point() +
geom_line(alpha = 0.3) +
facet_grid(MUT_ID~.) +
scale_y_continuous(limits = c(0, NA))
看起来像这样:
但是我需要从demo_mean_sd
数据框中添加标准差的行,并且意味着。看起来应该是这样的:
然而,由于缺少一个共同的x轴,我一直在试图让线条在绘图上绘制时遇到问题。例如,我尝试过这样的事情:
ggplot(data = demo_df,
aes(y = FREQ, x = Sample_Run, color = MUT_ID, group = MUT_ID) ) +
geom_point() +
geom_line(alpha = 0.3) +
facet_grid(MUT_ID~.) +
scale_y_continuous(limits = c(0, NA)) +
geom_ribbon(data = demo_mean_sd, aes(ymin = sd_interval_lower, ymax = sd_interval_upper))
错误:
Error in eval(expr, envir, enclos) : object 'Sample_Run' not found
由于刻面,我无法弄清楚如何使用其他绘图类型。
答案 0 :(得分:2)
一种方法是在创建绘图之前合并数据框。您可以使用dplyr在MUT_ID上合并以下内容:
ggplot(data = demo_df_merged,
aes(x = Sample_Run, color = MUT_ID, group = MUT_ID) ) +
geom_point(aes(y = FREQ)) +
geom_line(alpha = 0.3, aes(y = FREQ)) +
geom_line(aes(y = mean)) +
geom_line(size = 2, aes(y = sd_interval_upper)) +
geom_line(size = 2, aes(y = sd_interval_lower)) +
facet_grid(MUT_ID~.) +
scale_y_continuous(limits = c(0, NA))
合并后,您可以使用geom_line绘制上限和下限以及平均值
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
///This class is use just to check if the Image returned by
///the PictureRecorder of the first Canvas is not empty.
class CanvasImageDraw extends CustomPainter {
ui.Image image;
CanvasImageDraw(this.image);
@override
void paint(ui.Canvas canvas, ui.Size size) {
// simple aspect fit for the image
var hr = size.height / image.height;
var wr = size.width / image.width;
double ratio;
double translateX;
double translateY;
if (hr < wr) {
ratio = hr;
translateX = (size.width - (ratio * image.width)) / 2;
translateY = 0.0;
} else {
ratio = wr;
translateX = 0.0;
translateY = (size.height - (ratio * image.height)) / 2;
}
canvas.translate(translateX, translateY);
canvas.scale(ratio, ratio);
canvas.drawImage(image, new Offset(0.0, 0.0), new Paint());
}
@override
bool shouldRepaint(CanvasImageDraw oldDelegate) {
return image != oldDelegate.image;
}
}
///Class used to display the second canvas
class SecondView extends StatelessWidget {
ui.Image image;
var pictureRecorder = new ui.PictureRecorder();
SecondView({this.image});
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Image Debug'),
),
body: new Column(
children: <Widget>[
new Text('Top'),
CustomPaint(
painter: new CanvasImageDraw(image),
size: new Size(200.0, 200.0),
),
new Text('Bottom'),
],
));
}
}
///This is the CustomPainter of the first Canvas which is used
///to draw to display the users draw/sign.
class SignaturePainter extends CustomPainter {
final List<Offset> points;
final int revision;
SignaturePainter(this.points, [this.revision = 0]);
void paint(canvas, Size size) {
if (points.length < 2) return;
Paint paint = new Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != null && points[i + 1] != null)
canvas.drawLine(points[i], points[i + 1], paint);
}
}
// simplified this, but if you ever modify points instead of changing length you'll
// have to revise.
bool shouldRepaint(SignaturePainter other) => other.revision != revision;
}
///Classes used to get the user draw/sign, send it to the first canvas,
///then display it.
///'points' list of position returned by the GestureDetector
class Signature extends StatefulWidget {
Signature({Key key}): super(key: key);
@override
State<StatefulWidget> createState()=> new SignatureState();
}
class SignatureState extends State<Signature> {
List<Offset> _points = <Offset>[];
int _revision = 0;
ui.Image get rendered {
var pictureRecorder = new ui.PictureRecorder();
Canvas canvas = new Canvas(pictureRecorder);
SignaturePainter painter = new SignaturePainter(_points);
var size = context.size;
// if you pass a smaller size here, it cuts off the lines
painter.paint(canvas, size);
// if you use a smaller size for toImage, it also cuts off the lines - so I've
// done that in here as well, as this is the only place it's easy to get the width & height.
return pictureRecorder.endRecording().toImage(size.width.floor(), size.height.floor());
}
void _addToPoints(Offset position) {
_revision++;
_points.add(position);
}
Widget build(BuildContext context) {
return new Stack(
children: [
GestureDetector(
onPanStart: (DragStartDetails details) {
RenderBox referenceBox = context.findRenderObject();
Offset localPosition = referenceBox.globalToLocal(details.globalPosition);
setState(() {
_addToPoints(localPosition);
});
},
onPanUpdate: (DragUpdateDetails details) {
RenderBox referenceBox = context.findRenderObject();
Offset localPosition = referenceBox.globalToLocal(details.globalPosition);
setState(() {
_addToPoints(localPosition);
});
},
onPanEnd: (DragEndDetails details) => setState(() => _addToPoints(null)),
),
CustomPaint(painter: new SignaturePainter(_points, _revision)),
],
);
}
}
///The main class which display the first Canvas, Drawing/Signing area
///
///Floating action button used to stop the PictureRecorder's recording,
///then send the Picture to the next view/Canvas which should display it
class DemoApp extends StatelessWidget {
GlobalKey<SignatureState> signatureKey = new GlobalKey();
Widget build(BuildContext context) => new Scaffold(
body: new Signature(key: signatureKey),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.save),
onPressed: () async {
var image = signatureKey.currentState.rendered;
Navigator.push(context, new MaterialPageRoute(builder: (context) => new SecondView(image: image)));
},
),
);
}
void main() => runApp(new MaterialApp(home: new DemoApp()));
答案 1 :(得分:2)
首先,ggplot2
在所有数据都在一个长格式数据帧中时效果最佳,因此我会避免在第一个实例中创建更多数据帧。
其次,我会以不同的方式显示这些数据。丝带表示沿x轴的某种进展,通常是时间,这不是这里的情况。如果您对突变的重复方法感兴趣,我建议x轴应为MUT_ID
,重复应为点,然后使用stat_summary
添加用误差线表示。
这样的事情:
library(tidyverse)
# mean_sdl gives you the mean + 2 SD
demo_df %>%
ggplot(aes(MUT_ID, FREQ)) +
geom_jitter(aes(color = Sample_Run),
width = 0.2,
size = 2) +
stat_summary(fun.data = mean_sdl,
geom = "errorbar",
width = 0.2) +
stat_summary(fun.y = mean,
geom = "point",
fill = "red",
size = 4,
shape = 23) +
theme_bw()