//--------------------------------------------------------------------------------
//
// Copyright © The University of Queensland, 2012-2014. All rights reserved.
//
// License:
//--------------------------------------------------------------------------------
namespace Data
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text.RegularExpressions;
using Shared;
using Genomics;
///
/// Wraps R plotting to take in C# data and produce pdf output.
///
public static class Plots
{
///
/// Gets the legend from a ggplot object returned in a variable called 'legend'
///
/// The legend.
/// Ggplot variable.
public static string GetLegend(string legendVar, string ggplotVar)
{
return string.Join("\n", new string[]
{
"g_legend<-function(a.gplot){ ",
" tmp <- ggplot_gtable(ggplot_build(a.gplot)) ",
" leg <- which(sapply(tmp$grobs, function(x) x$name) == \"guide-box\")",
" legend <- tmp$grobs[[leg]] ",
" return(legend)} ",
string.Format("{0} <- g_legend({1}) ", legendVar, ggplotVar),
});
}
///
/// Applies theme options and overrides
///
/// The options.
/// Manual overrides.
/// Description.
public static string ThemeOptions(Dictionary manualOverrides, PlotDescription description = null)
{
var basicOptions = new Dictionary
{
{ "panel.background", "element_blank()" },
{ "panel.grid.major", "element_line(color='grey60')" },
{ "panel.grid.minor", "element_blank()" },
{ "axis.text.x", "element_text(color=\"#000000\", size=14)" },
{ "axis.text.y", "element_text(color=\"#000000\", size=14)" },
{ "axis.title.x", "element_text(face=\"bold\", size=18)" },
{ "axis.title.y", "element_text(face=\"bold\", size=18)" },
{ "legend.background", "element_blank()" },
{ "legend.position", "'bottom'" },
{ "legend.direction", "'horizontal'" },
{ "legend.box", "'vertical'" },
{ "legend.title", "element_blank()" },
};
Action> OverrideOptions = (overrides) =>
{
foreach (var v in overrides)
{
if (basicOptions.ContainsKey(v.Key))
{
if (string.IsNullOrEmpty(v.Value))
{
basicOptions.Remove(v.Key);
}
else
{
basicOptions[v.Key] = v.Value;
}
}
else if (!string.IsNullOrEmpty(v.Value))
{
basicOptions.Add(v.Key, v.Value);
}
}
};
if (description != null)
{
OverrideOptions(description.ThemeOverrides);
}
OverrideOptions(manualOverrides);
return " theme(" + string.Join(", ", basicOptions.Select(x => x.Key + "=" + x.Value)) + ")";
}
///
/// Places the legend at the bottom of the plot.
///
/// The legend.
public static string BottomLegend(PlotDescription description)
{
return ThemeOptions(new Dictionary
{
{ "legend.position", "'bottom'" },
{ "legend.direction", "'vertical'" },
{ "legend.box", "'horizontal'" },
}
.Concat(LegendTitle(description))
.ToDictionary(x => x.Key, x => x.Value),
description);
}
///
/// Places the legend at the bottom of the plot.
///
/// The legend.
public static string BottomLegendHV(PlotDescription description)
{
return ThemeOptions(new Dictionary
{
{ "legend.position", "'bottom'" },
{ "legend.direction", "'horizontal'" },
{ "legend.box", "'vertical'" },
}
.Concat(LegendTitle(description))
.ToDictionary(x => x.Key, x => x.Value),
description);
}
///
/// Places the legend at the bottom of the plot.
///
/// The legend.
public static string BottomLegendHV()
{
return " theme(legend.position=\"bottom\", legend.direction='horizontal', legend.box='vertical')";
}
///
/// Places the legend at the bottom of the plot.
///
/// The legend.
public static string BottomLegendHorizontal(PlotDescription description)
{
return ThemeOptions(new Dictionary
{
{ "legend.position", "'bottom'" },
{ "legend.direction", "'horizontal'" },
{ "legend.box", "'horizontal'" },
}
.Concat(LegendTitle(description))
.ToDictionary(x => x.Key, x => x.Value),
description);
}
///
/// Places the legend at the bottom of the plot.
///
/// The legend.
public static string BottomLegendHorizontal()
{
return " theme(legend.position=\"bottom\", legend.direction='horizontal', legend.box='horizontal')";
}
///
/// Hides the legend.
///
/// The legend.
public static string HideLegend(PlotDescription description = null)
{
return ThemeOptions(new Dictionary
{
{ "legend.position", "'none'" },
},
description);
}
///
/// Starts the plot.
///
/// The plot.
/// Plot file.
/// Width.
/// Height.
public static string StartPlot(string plotFile, double width, double height)
{
return string.Format("pdf(file='{0}', width={1}, height={2})", plotFile, width, height);
}
///
/// Starts the plot.
///
/// The plot.
/// Description.
public static string StartPlot(PlotDescription description)
{
return StartPlot(description.PlotFile, description.Width, description.Height);
}
///
/// Starts the legend.
///
/// The legend.
/// Description.
public static string StartLegend(PlotDescription description)
{
return StartPlot(description.LegendFile, description.LegendWidth, description.LegendHeight);
}
///
/// Runs a ggplot with the given lines and legend data.
///
/// The plot.
/// Description.
/// Plot lines.
public static string[] GGPlot(PlotDescription description, string[] plotLines, string dataVar = default(string))
{
string plotVar = R.Variables.Plot;
return new string[]
{
StartPlot(description.PlotFile, description.Width, description.Height),
CreateGGPlot(plotVar, dataVar),
R.JoinLines(plotLines),
description.SeparateLegend ? HideLegend(description) : FinishGGPlot(description),
plotVar,
EndPlot()
};
}
///
/// Creates a ggplot legend in a separate file if the description calls for it.
///
/// The legend.
/// Description.
/// Plot lines.
public static string[] GGLegend(PlotDescription description, string[] plotLines, string dataVar = default(string))
{
if (description.LegendFile != null)
{
string plotVar = R.Variables.Plot;
string legendVar = R.Variables.Legend;
return new string[]
{
StartPlot(description.LegendFile, description.LegendWidth, description.LegendHeight),
CreateGGPlot(plotVar, dataVar),
R.JoinLines(plotLines),
description.LegendDirection == PlotDescription.Direction.Horizontal ? BottomLegendHV(description) : FinishGGPlot(description),
GetLegend(legendVar, plotVar),
legendVar,
string.Format("grid.draw({0})", legendVar),
EndPlot()
};
}
else
{
return null;
}
}
///
/// GGs the plot.
///
/// The plot.
/// Variable.
public static string CreateGGPlot(string plotVariable, string dataVariable = default(string))
{
if (string.IsNullOrEmpty(dataVariable))
{
dataVariable = string.Empty;
}
return string.Format("{0} <- ggplot({1}) + ", plotVariable, dataVariable);
}
///
/// Labels the axes.
///
/// The axes.
/// Description.
public static string LabelAxes(PlotDescription description)
{
string labels = string.Empty;
Action> addLabel = (name, axis) =>
{
var a = axis(description);
if (a != null && !string.IsNullOrEmpty(a.Label))
{
labels += LabelAxis(name, a.Label, !a.UnquotedLabel);
}
};
addLabel("x", d => d.XAxis);
addLabel("y", d => d.YAxis);
return labels;
}
///
/// Color the specified description.
///
/// Description.
public static string Color(PlotDescription description)
{
return description.Colors == null ?
description.RgbColors == null ?
string.Empty :
"scale_color_manual(values=c('" + string.Join(", '", description.RgbColors) + ")) + " :
"scale_color_manual(values=c('" + string.Join("', '", description.Colors) + "')) + ";
}
///
/// Colors the fill.
///
/// The fill.
/// Description.
public static string ColorFill(PlotDescription description)
{
return description.FillColors == null ?
description.FillRgbColors == null ?
string.Empty :
"scale_fill_manual(values=c('" + string.Join(", '", description.FillRgbColors) + ")) + " :
"scale_fill_manual(values=c('" + string.Join("', '", description.FillColors) + "')) + ";
}
///
/// Labels the axis.
///
/// The axis.
/// Axis.
/// Label.
/// If set to true quoted.
private static string LabelAxis(string axis, string label, bool quoted)
{
string delim = string.Empty;
if (quoted)
{
delim = "'";
}
return string.Format(" {0}lab({1}{2}{3}) + ", axis, delim, label, delim);
}
private static List> LegendTitle(PlotDescription description)
{
return string.IsNullOrEmpty(description.FillLabel) ?
new List>() :
new List> { new KeyValuePair("legend.title", "'" + description.FillLabel + "'") };
}
///
/// Finishes the GG plot.
///
/// The GG plot.
public static string FinishGGPlot(PlotDescription description)
{
return ThemeOptions(new Dictionary
{
}
.Concat(LegendTitle(description))
.ToDictionary(x => x.Key, x => x.Value),
description);
}
///
/// Finishes the GG plot.
///
/// The GG plot.
public static string FinishGGPlot()
{
return ThemeOptions(new Dictionary{ });
}
///
/// Ends the plot.
///
/// The plot.
public static string EndPlot()
{
return "dev.off()";
}
///
/// Create script lines based on the YNames fields
///
/// The to Y names.
/// Description.
/// Code generator.
public static string[] ApplyToYNames(
PlotDescription description,
Func codeGenerator)
{
return description.YNames.Select((name, i) => codeGenerator(name, i)).ToArray();
}
///
/// Plots the histone profile.
///
/// Filename.
/// Cell line.
/// Expression type.
/// Histone type.
public static void PlotHistoneProfile(string filename, string tissue, string expressionType, string histoneType)
{
string createPng =
"png(file=\"results/HistoneProfile/profiles/" + tissue + "." + expressionType + "." + histoneType + ".png\", width=600, height=400)\n";
string createPdf =
"pdf(file=\"results/HistoneProfile/profiles/" + tissue + "." + expressionType + "." + histoneType + ".pdf\", width=7.5, height=5)\n";
string load =
"x <- read.table(\"" + filename + "\")\n" +
"ccc <- c(rgb(0,1,0,0.8), rgb(0,1,1,0.8))\n";
string plot =
"ymax <- 0.75; if (max(x[,2:3]) > ymax) { ymax <- 0.75 }\n" +
"plot(x[,1], x[,2], col=ccc[1], ylim=c(0, ymax), xlim=c(-10000, 10000), xlab=\"Distance from TSS\", ylab=\"Fraction of Peaks\")\n" +
"points(x[,1], x[,3], col=ccc[2])\n" +
"title(\"Promoter Distribution of " + histoneType + " peaks in " + tissue + " " + expressionType + " Map\")\n" +
"legend(\"topleft\", title=\"TSS Set\", c(\"Variantly Expressed\", \"Map\"), fill=ccc)\n" +
"dev.off()\n";
string scriptName = "../temp/plotHistoneProfile" + tissue + expressionType + histoneType + ".R";
using (TextWriter tw = Helpers.CreateStreamWriter(scriptName))
{
tw.WriteLine(load + createPng + plot + createPdf + plot);
tw.WriteLine("q(save=\"no\")");
}
R.RunScript(scriptName, "../temp/junk");
}
///
/// Plots the distributions.
///
/// Description.
public static void Distributions(PlotDescription description)
{
description.Validate();
const string Mids = "mids";
const string Frequency = "frequency";
var createHistograms = ApplyToYNames(
description,
(name, i) => Functions.Histogram(
R.Variables.M + i,
R.Variables.X,
name,
description.XAxis.Breaks, true));
var createFrequencies = ApplyToYNames(
description,
(name, i) => R.BindColumns(
"data" + i,
new string[] { R.Variables.M + i, R.Variables.M + i },
new string[] { Mids, Frequency }));
var plotCurves = ApplyToYNames(
description, (name, i) => string.Format(
" geom_line(aes(x={0}, y={1}, colour={2}), {3}) +",
Mids,
Frequency,
GetColor(description, i),
"data" + i));
string[] plotLines = GGPlot(description, new string[]
{
R.JoinLines(plotCurves),
LabelAxes(description),
});
string[] legendLines = GGLegend(description, plotCurves);
R.WriteAndRunScript(new string[]
{
R.LoadLibrary(R.Libraries.GGPlot),
R.LoadLibrary(R.Libraries.GridExtra),
R.ReadTable(R.Variables.X, description.TableFile, true),
R.JoinLines(createHistograms),
R.JoinLines(createFrequencies),
R.JoinLines(plotLines),
R.JoinLines(legendLines),
},
description.ScriptFile);
}
///
/// Densities the specified description.
///
/// Description.
public static void Densities(PlotDescription description)
{
description.Validate();
if (description.PlotVariable == null)
{
description.PlotVariable = R.Variables.X;
}
string densities = string.Format(
" geom_density(aes(x={0}, fill=factor({1}), colour=factor({1})), alpha={2}, {3}) +",
description.XName,
description.Factors[0],
description.Alpha,
description.PlotVariable);
var corePlot = new string[]
{
densities,
LabelAxes(description),
XCoordinateTransform(description.XAxis),
Color(description),
ColorFill(description),
};
string[] plotLines = GGPlot(description, corePlot);
string[] legendLines = GGLegend(description, corePlot);
R.WriteAndRunScript(new string[]
{
R.LoadLibrary(R.Libraries.GGPlot),
R.LoadLibrary(R.Libraries.GridExtra),
R.LoadLibrary(R.Libraries.Scales),
R.ReadTable(R.Variables.X, description.TableFile, true),
R.JoinLines(plotLines),
R.JoinLines(legendLines),
},
description.ScriptFile);
}
///
/// Creates a scatter plot.
///
/// Description.
public static void Scatter(PlotDescription description)
{
description.Validate();
if (description.PlotVariable == null)
{
description.PlotVariable = R.Variables.X;
}
var corePlot = ApplyToYNames(
description, (name, i) => string.Format(
" geom_point(aes(x={0}, y={1}, colour={2}{3}), {4}) +",
description.XName,
name,
description.Factors[0],
description.Size != 0 ? ", size=" + description.Size : string.Empty,
description.PlotVariable))
.Concat(new string[]
{
LabelAxes(description),
YCoordinateTransform(description.YAxis),
XCoordinateTransform(description.XAxis),
Color(description),
})
.ToArray();
string[] plotLines = GGPlot(description, corePlot);
string[] legendLines = GGLegend(description, corePlot);
R.WriteAndRunScript(new string[]
{
R.LoadLibrary(R.Libraries.GGPlot),
R.LoadLibrary(R.Libraries.Scales),
R.LoadLibrary(R.Libraries.GridExtra),
R.ReadTable(R.Variables.X, description.TableFile, true),
R.FactorColumn(R.Variables.X, description.Factors[0]),
R.JoinLines(plotLines),
R.JoinLines(legendLines),
},
description.ScriptFile);
}
///
/// Creates a boxplot of a set of values organized by a factor
///
/// Description.
/// If set to true violin.
/// If set to true hide outliers.
public static void BoxPlot(PlotDescription description, bool violin, bool hideOutliers = false)
{
description.Validate();
if (description.PlotVariable == null)
{
description.PlotVariable = R.Variables.X;
}
string function = violin ? "geom_violin" : "geom_boxplot";
var plotLines = new string[]
{
description.Factors.Length == 1 ?
string.Format(
" {0}(aes({1}, y={2}), width=0.8, {3}{4}) + ",
function,
description.FormatFactor(0),
description.YNames[0],
R.Variables.X,
hideOutliers ? ", outlier.shape=NA" : string.Empty) :
string.Format(
" {0}(aes({1}, fill={2}, y={3}), width=0.8, {4}{5}) + ",
function,
description.FormatFactor(0),
//description.FactorLevels != null && description.FactorLevels.Count >= 2 && description.FactorLevels[1] != null ?
description.FormatFactor(1),
description.YNames[0],
description.PlotVariable,
hideOutliers ? ", outlier.shape=NA" : string.Empty),
YCoordinateTransform(description.YAxis),
XCoordinateTransform(description.XAxis),
LabelAxes(description),
description.FlipCoordinates ? " coord_flip() + " : string.Empty,
ColorFill(description),
};
string[] corePlot = GGPlot(description, plotLines);
string[] legendLines = GGLegend(description, plotLines);
R.WriteAndRunScript(new string[]
{
R.LoadLibrary(R.Libraries.GGPlot),
R.LoadLibrary(R.Libraries.Scales),
R.LoadLibrary(R.Libraries.GridExtra),
R.ReadTable(R.Variables.X, description.TableFile, true),
R.FactorColumn(R.Variables.X, description.Factors[0]),
description.Factors.Length > 1 ? R.FactorColumn(R.Variables.X, description.Factors[1]) : string.Empty,
R.JoinLines(corePlot),
description.LegendFile != null ? R.JoinLines(legendLines) : string.Empty,
},
description.ScriptFile);
}
public static void LinePointPlot(PlotDescription description)
{
description.Validate();
int pointSize = description.Size != 0 ? description.Size : 2;
int lineSize = description.Size != 0 ? description.Size / 2 : 1;
Func plot = (function) => description.Factors.Length == 0 ?
string.Format(
" {0}(aes(x={1}, y={2}), size={3}, {4}) + ",
function,
description.XName,
description.YNames[0],
function == "geom_point" ? pointSize : lineSize,
R.Variables.X) :
string.Format(
" {0}(aes(x={1}, y={2}, color={3}), size={4}, {5}) + ",
function,
description.XName,
description.YNames[0],
description.FormatFactor(0),
function == "geom_point" ? pointSize : lineSize,
R.Variables.X);
var plotLines = new string[]
{
plot("geom_line"),
plot("geom_point"),
description.YNames.Length == 3 && description.Factors.Length > 0 ?
string.Format(
" {0}(aes(x={1}, ymin={2}, ymax={3}, fill={4}), alpha=0.5, {5}) + ",
"geom_ribbon",
description.XName,
description.YNames[1],
description.YNames[2],
description.FormatFactor(0),
R.Variables.X) :
string.Empty,
//description.YAxis.Min != 0 && description.YAxis.Max != 0 ?
//string.Format("coord_cartesian(ylim = c({0}, {1})) + ", description.YAxis.Min, description.YAxis.Max) :
//string.Empty,
YCoordinateTransform(description.YAxis),
XCoordinateTransform(description.XAxis),
LabelAxes(description),
ColorFill(description),
description.FlipCoordinates ? " coord_flip() + " : string.Empty,
};
string[] corePlot = GGPlot(description, plotLines);
string[] legendLines = GGLegend(description, plotLines);
R.WriteAndRunScript(new string[]
{
R.LoadLibrary(R.Libraries.GGPlot),
R.LoadLibrary(R.Libraries.Scales),
R.LoadLibrary(R.Libraries.GridExtra),
R.ReadTable(R.Variables.X, description.TableFile, true),
description.Factors.Length > 0 ? R.FactorColumn(R.Variables.X, description.Factors[0]) : string.Empty,
description.Factors.Length > 1 ? R.FactorColumn(R.Variables.X, description.Factors[1]) : string.Empty,
R.JoinLines(corePlot),
description.LegendFile != null ? R.JoinLines(legendLines) : string.Empty,
},
description.ScriptFile);
}
///
/// Creates a boxplot of a set of values organized by a factor
///
/// Description.
public static void FactorBarPlot(PlotDescription description, bool whiskers)
{
description.Validate();
if (description.PlotVariable == null)
{
description.PlotVariable = R.Variables.X;
}
var plotLines = new string[]
{
description.Factors.Length == 1 ?
string.Format(
" geom_bar(aes(x=factor({0}), y={1}), position='dodge', stat='identity', width=0.8, {2}) + ",
description.Factors[0],
description.YNames[0],
description.PlotVariable) :
string.Format(
" geom_bar(aes(x={0}, fill={1}, y={2}), position='dodge', stat='identity', width=0.6, {3}) + ",
description.FormatFactor(0),
description.FormatFactor(1),
description.YNames[0],
description.PlotVariable),
whiskers ? string.Format(
" geom_errorbar(aes(x={0}, ymin={1}-{2}, ymax={1}+{2}, fill={3}), position='dodge', stat='identity', width=0.6, {4}) + ",
description.FormatFactor(0),
description.YNames[0],
description.YNames[1],
description.FormatFactor(1),
description.PlotVariable) : string.Empty,
LabelAxes(description),
YCoordinateTransform(description.YAxis),
ColorFill(description),
};
string[] corePlot = GGPlot(description, plotLines);
string[] coreLegend = GGLegend(description, plotLines);
R.WriteAndRunScript(new string[]
{
R.LoadLibrary(R.Libraries.GGPlot),
R.LoadLibrary(R.Libraries.GridExtra),
R.LoadLibrary(R.Libraries.Scales),
R.ReadTable(R.Variables.X, description.TableFile, true),
R.FactorColumn(R.Variables.X, description.Factors[0]),
description.Factors.Length > 1 ? R.FactorColumn(R.Variables.X, description.Factors[1]) : string.Empty,
R.JoinLines(corePlot),
description.LegendFile != null ? R.JoinLines(coreLegend) : string.Empty,
},
description.ScriptFile);
}
///
/// Creates a CDF of the given X value organized by factor
///
/// Description.
public static void FactorCDFPlot(PlotDescription description)
{
description.Validate();
string[] corePlot = GGPlot(description, new string[]
{
description.Factors.Length == 1 ?
string.Format(
" stat_ecdf(aes(x={0}, color={1}), {2}) + ",
description.XName,
description.Factors[0],
R.Variables.X) :
LabelAxes(description),
});
R.WriteAndRunScript(new string[]
{
R.LoadLibrary(R.Libraries.GGPlot),
R.LoadLibrary(R.Libraries.GridExtra),
R.ReadTable(R.Variables.X, description.TableFile, true),
R.FactorColumn(R.Variables.X, description.Factors[0]),
R.JoinLines(corePlot),
},
description.ScriptFile);
}
///
/// Gets the color.
///
/// The color.
/// Description.
/// Index.
private static string GetColor(PlotDescription description, int index)
{
if (description.ColorNames == null)
{
return string.Format("'{0}'", index + 1);
}
else
{
return description.ColorNames[index];
}
}
private static string CoordinateTransform(AxisDescription axis, string axis_name)
{
if (axis.Breaks == null && axis.DiscreteBreaks == null && axis.BreakLabels == null &&
axis.CoordinateTransform == AxisDescription.CoordTransform.None)
{
return string.Empty;
}
var breakData = string.Empty;
Func FormatBreaks = (breaks, labels) => labels != null ?
string.Format("breaks = c({0}), labels = c({1})",
string.Join(", ", breaks),
string.Join(", ", labels)) :
string.Format("breaks = c({0})",
string.Join(", ", breaks));
if (axis.Breaks != null)
{
breakData = FormatBreaks(axis.Breaks.Select(x => x.ToString()).ToArray(), axis.BreakLabels);
}
else if (axis.DiscreteBreaks != null)
{
breakData = FormatBreaks(axis.DiscreteBreaks, axis.BreakLabels);
}
else if (axis.BreakLabels != null)
{
breakData = string.Format("breaks = c('{0}')",
string.Join("', '", axis.BreakLabels));
}
var transformData = axis.CoordinateTransform == AxisDescription.CoordTransform.None ||
axis.CoordinateTransform == AxisDescription.CoordTransform.Discrete ?
string.Empty :
string.Format("trans=log{0}_trans()", axis.CoordinateTransformBase);
var limitData = (axis.Min != 0 || axis.Max != 0) && axis.Max > axis.Min ?
string.Format("limits=c({0}, {1})", axis.Min, axis.Max) :
string.Empty;
return string.Format(
" scale_{0}_{1}({2}) + ",
axis_name,
axis.CoordinateTransform == AxisDescription.CoordTransform.Discrete ? "discrete" : "continuous",
string.Join(", ", new string[]
{
transformData,
breakData,
limitData,
}.Where(x => !string.IsNullOrEmpty(x))));
}
public static string YCoordinateTransform(AxisDescription axis)
{
return CoordinateTransform(axis, "y");
}
public static string XCoordinateTransform(AxisDescription axis)
{
return CoordinateTransform(axis, "x");
}
}
}