Skip to content

Commit

Permalink
Quantile Parameters (#689)
Browse files Browse the repository at this point in the history
* Use quantile stat var in the geom_violin().

* Fix test for the YDensityStat.

* Fix quantile line for the last quantile for the geom_violin().

* Fix tooltips color when quantiles using for the geom_violin().

* Fix docstrings for the geom_violin() function.

* Tiny fix in the docstrings. Update future_changes.md file.

* Add quantiles to the geom_area() and geom_density().

* Hide quantile aesthetic for the all geometries that are uses it.

* Update docstrings for the geom_density() function.

* Tiny refactor fixes.

* Update future_changes.md.

* Rename param 'quantiles' back to 'draw_quantiles' for the geom_violin().

* Refactor using of the QuantilesHelper.

* Use grouping to remove extra lines for the area geometry.

* Fix issue #658.

* Tiny fix in the future_changes.md file.

* Tiny fixes after merging with the master branch.

* In the geom_violin() parameter 'draw_quantiles' renamed to 'quantiles'.

* Add example to AreaBatik.

* Use expandByGroupEnds() in DensityStat. Add StackingType and use LN type for the AreaGeom.

* Small fixes in code style in geometries.

* Remove extra parameters from the AreaGeom.

* Refactor DensityStatUtil and QuantilesHelper (mostly about visibility).

* Upgrade spec descriptions in demo.

* Small fixes in code.

* Refactor DensityStatUtil.expandByGroupEnds and add some tests for it.

* Add tests for the QuantilesHelper and refactor it.

* Remove extra borders simplification from AreaGeom and ViolinGeom.

* Fix throwing messages in the BogusContext.

* Refactor QuantilesHelper.

* Refactor DensityStatUtil.calculateStatQuantile().

* Tiny fix in StatProto.

* Refactor DensityStatUtil.

* Refactor StackPos.
  • Loading branch information
ASmirnov-HORIS committed Feb 9, 2023
1 parent fa7da94 commit 787c4b5
Show file tree
Hide file tree
Showing 35 changed files with 1,019 additions and 535 deletions.
7 changes: 7 additions & 0 deletions future_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,14 @@

### Changed

- **Breaking change** in `geom_violin()`: parameter `draw_quantiles` renamed to `quantiles` - and now it works as in the `geom_area_ridges()` geometry.

- `geom_violin()`: added `quantile_lines` parameter - as in the `geom_area_ridges()` geometry. Also, it was added a `..quantile..` statistic variable.

- `geom_density()`: added two new parameters - `quantiles` and `quantile_lines` - as in the `geom_area_ridges()` geometry. Also, it was added a `..quantile..` statistic variable.

- `pandas` library was added to dependencies of the `residual_plot()` function.

- Python packages for `Windows` no longer require `MinGW` tools to run.
- parameter `flat=True` turns off lines re-projection, keeping the original number of points.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ object GeomMeta {

private val AREA = listOf(
Aes.X, Aes.Y,
Aes.QUANTILE,
Aes.SIZE,
Aes.LINETYPE,
Aes.COLOR,
Expand Down Expand Up @@ -238,6 +239,7 @@ object GeomMeta {
Aes.X,
Aes.Y,
Aes.VIOLINWIDTH,
Aes.QUANTILE,

Aes.ALPHA,
Aes.COLOR,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ import jetbrains.datalore.plot.base.geom.util.MultiPointDataConstructor.singlePo
import jetbrains.datalore.plot.base.interact.GeomTargetCollector.TooltipParams
import jetbrains.datalore.plot.base.interact.TipLayoutHint
import jetbrains.datalore.plot.base.render.SvgRoot
import jetbrains.datalore.plot.base.stat.DensityStat
import jetbrains.datalore.vis.svg.SvgLineElement

open class AreaGeom : GeomBase() {
var quantiles: List<Double> = DensityStat.DEF_QUANTILES
var quantileLines: Boolean = DEF_QUANTILE_LINES

protected fun dataPoints(aesthetics: Aesthetics): Iterable<DataPointAesthetics> {
return GeomUtil.ordered_X(aesthetics.dataPoints())
Expand All @@ -31,22 +35,53 @@ open class AreaGeom : GeomBase() {
val dataPoints = dataPoints(aesthetics)

val helper = LinesHelper(pos, coord, ctx)
val paths = helper.createBands(dataPoints, GeomUtil.TO_LOCATION_X_Y, GeomUtil.TO_LOCATION_X_ZERO)
// If you want to retain the side edges of area: comment out the following codes,
// and switch decorate method in LinesHelper.createBands
helper.setAlphaEnabled(false)
val lines = helper.createLines(dataPoints, GeomUtil.TO_LOCATION_X_Y)
paths.zip(lines).asReversed().forEach { (path, line) ->
appendNodes(listOf(path, line), root)

dataPoints.sortedByDescending(DataPointAesthetics::group).groupBy(DataPointAesthetics::group).forEach { (_, groupDataPoints) ->
groupDataPoints.groupBy(DataPointAesthetics::fill).forEach { (_, points) ->
val paths = helper.createBands(points, GeomUtil.TO_LOCATION_X_Y, GeomUtil.TO_LOCATION_X_ZERO)
// If you want to retain the side edges of area: comment out the following codes,
// and switch decorate method in LinesHelper.createBands
appendNodes(paths, root)
}

helper.setAlphaEnabled(false)
groupDataPoints.groupBy(DataPointAesthetics::color).forEach { (_, points) ->
appendNodes(helper.createLines(points, GeomUtil.TO_LOCATION_X_Y), root)
}

if (quantileLines) {
createQuantileLines(groupDataPoints, pos, coord, ctx).forEach { quantileLine ->
root.add(quantileLine)
}
}

groupDataPoints.groupBy { Pair(it.color(), it.fill()) }.forEach { (_, points) ->
buildHints(points, pos, coord, ctx)
}
}
}

buildHints(aesthetics, pos, coord, ctx)
private fun createQuantileLines(
dataPoints: Iterable<DataPointAesthetics>,
pos: PositionAdjustment,
coord: CoordinateSystem,
ctx: GeomContext
): List<SvgLineElement> {
val quantilesHelper = QuantilesHelper(pos, coord, ctx, quantiles)
val definedPoints = GeomUtil.withDefined(dataPoints, Aes.X, Aes.Y)
val toLocationBoundStart: (DataPointAesthetics) -> DoubleVector = { p ->
GeomUtil.TO_LOCATION_X_Y(p)!!
}
val toLocationBoundEnd: (DataPointAesthetics) -> DoubleVector = { p ->
GeomUtil.TO_LOCATION_X_ZERO(p)!!
}
return quantilesHelper.getQuantileLineElements(definedPoints, Aes.X, toLocationBoundStart, toLocationBoundEnd)
}

private fun buildHints(aesthetics: Aesthetics, pos: PositionAdjustment, coord: CoordinateSystem, ctx: GeomContext) {
private fun buildHints(dataPoints: Iterable<DataPointAesthetics>, pos: PositionAdjustment, coord: CoordinateSystem, ctx: GeomContext) {
val geomHelper = GeomHelper(pos, coord, ctx)
val multiPointDataList = MultiPointDataConstructor.createMultiPointDataByGroup(
aesthetics.dataPoints(),
dataPoints,
singlePointAppender { p -> toClient(geomHelper, p) },
reducer(0.999, false)
)
Expand Down Expand Up @@ -92,6 +127,8 @@ open class AreaGeom : GeomBase() {
// Aes.ALPHA
// )

const val DEF_QUANTILE_LINES = false

const val HANDLES_GROUPS = true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ import jetbrains.datalore.plot.base.geom.util.*
import jetbrains.datalore.plot.base.interact.GeomTargetCollector
import jetbrains.datalore.plot.base.interact.TipLayoutHint
import jetbrains.datalore.plot.base.render.SvgRoot
import jetbrains.datalore.plot.base.stat.DensityRidgesStat
import jetbrains.datalore.plot.common.data.SeriesUtil
import jetbrains.datalore.vis.svg.SvgLineElement

class AreaRidgesGeom : GeomBase(), WithHeight {
var scale: Double = DEF_SCALE
var minHeight: Double = DEF_MIN_HEIGHT
var quantiles: List<Double> = DensityRidgesStat.DEF_QUANTILES
var quantileLines: Boolean = DEF_QUANTILE_LINES

override fun buildIntern(
Expand Down Expand Up @@ -86,48 +89,27 @@ class AreaRidgesGeom : GeomBase(), WithHeight {
appendNodes(helper.createLines(points, boundTransform), root)
}

if (quantileLines) drawQuantileLines(root, dataPoints, pos, coord, ctx)

buildHints(dataPoints, ctx, helper, boundTransform)
}
if (quantileLines) {
createQuantileLines(dataPoints, pos, coord, ctx).forEach { quantileLine ->
root.add(quantileLine)
}
}

private fun drawQuantileLines(
root: SvgRoot,
dataPoints: Iterable<DataPointAesthetics>,
pos: PositionAdjustment,
coord: CoordinateSystem,
ctx: GeomContext
) {
val pIt = dataPoints.sortedWith(
compareBy(
DataPointAesthetics::group,
DataPointAesthetics::quantile,
DataPointAesthetics::x
)
).iterator()
if (!pIt.hasNext()) return
var pPrev = pIt.next()
while (pIt.hasNext()) {
val pCurr = pIt.next()
val quantilesAreSame = pPrev.quantile() == pCurr.quantile() ||
(pPrev.quantile()?.isFinite() != true && pCurr.quantile()?.isFinite() != true)
if (!quantilesAreSame) drawQuantileLine(root, pCurr, pos, coord, ctx)
pPrev = pCurr
dataPoints.groupBy { Pair(it.color(), it.fill()) }.forEach { (_, points) ->
buildHints(points, ctx, helper, boundTransform)
}
}

private fun drawQuantileLine(
root: SvgRoot,
dataPoint: DataPointAesthetics,
private fun createQuantileLines(
dataPoints: Iterable<DataPointAesthetics>,
pos: PositionAdjustment,
coord: CoordinateSystem,
ctx: GeomContext
) {
val svgElementHelper = GeomHelper(pos, coord, ctx).createSvgElementHelper()
val start = toLocationBound(ctx)(dataPoint)
val end = DoubleVector(dataPoint.x()!!, dataPoint.y()!!)
val line = svgElementHelper.createLine(start, end, dataPoint)!!
root.add(line)
): List<SvgLineElement> {
val quantilesHelper = QuantilesHelper(pos, coord, ctx, quantiles, Aes.Y)
val toLocationBoundStart = toLocationBound(ctx)
val toLocationBoundEnd = { p: DataPointAesthetics -> DoubleVector(p.x()!!, p.y()!!) }
return quantilesHelper.getQuantileLineElements(dataPoints, Aes.X, toLocationBoundStart, toLocationBoundEnd)
}

private fun toLocationBound(ctx: GeomContext): (p: DataPointAesthetics) -> DoubleVector {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class DensityGeom : AreaGeom() {

companion object {
// val RENDERS: List<Aes<*>> = AreaGeom.RENDERS
const val DEF_QUANTILE_LINES =
AreaGeom.DEF_QUANTILE_LINES

const val HANDLES_GROUPS =
AreaGeom.HANDLES_GROUPS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,18 @@ import jetbrains.datalore.plot.base.geom.util.*
import jetbrains.datalore.plot.base.interact.GeomTargetCollector
import jetbrains.datalore.plot.base.interact.TipLayoutHint
import jetbrains.datalore.plot.base.render.SvgRoot
import jetbrains.datalore.plot.base.stat.YDensityStat
import jetbrains.datalore.vis.svg.SvgLineElement

class ViolinGeom : GeomBase() {
var quantiles: List<Double> = YDensityStat.DEF_QUANTILES
var quantileLines: Boolean = DEF_QUANTILE_LINES
var showHalf: Double = DEF_SHOW_HALF
private var drawQuantiles: List<Double> = DEF_DRAW_QUANTILES
private val negativeSign: Double
get() = if (showHalf > 0.0) 0.0 else -1.0
private val positiveSign: Double
get() = if (showHalf < 0.0) 0.0 else 1.0

fun setDrawQuantiles(quantiles: List<Double>) {
drawQuantiles = quantiles
}

override fun buildIntern(
root: SvgRoot,
aesthetics: Aesthetics,
Expand Down Expand Up @@ -58,36 +57,43 @@ class ViolinGeom : GeomBase() {
val leftBoundTransform = toLocationBound(negativeSign, ctx)
val rightBoundTransform = toLocationBound(positiveSign, ctx)

val paths = helper.createBands(dataPoints, leftBoundTransform, rightBoundTransform)
appendNodes(paths, root)
dataPoints.groupBy(DataPointAesthetics::fill).forEach { (_, points) ->
val paths = helper.createBands(points, leftBoundTransform, rightBoundTransform)
appendNodes(paths, root)
}

helper.setAlphaEnabled(false)
appendNodes(helper.createLines(dataPoints, leftBoundTransform), root)
appendNodes(helper.createLines(dataPoints, rightBoundTransform), root)
dataPoints.groupBy(DataPointAesthetics::color).forEach { (_, points) ->
appendNodes(helper.createLines(points, leftBoundTransform), root)
appendNodes(helper.createLines(points, rightBoundTransform), root)
}

buildQuantiles(root, dataPoints, pos, coord, ctx)
if (quantileLines) {
createQuantileLines(dataPoints, pos, coord, ctx).forEach { quantileLine ->
root.add(quantileLine)
}
}

buildHints(dataPoints, ctx, helper, leftBoundTransform)
buildHints(dataPoints, ctx, helper, rightBoundTransform)
dataPoints.groupBy { Pair(it.color(), it.fill()) }.forEach { (_, points) ->
buildHints(points, ctx, helper, leftBoundTransform)
buildHints(points, ctx, helper, rightBoundTransform)
}
}

private fun buildQuantiles(
root: SvgRoot,
private fun createQuantileLines(
dataPoints: Iterable<DataPointAesthetics>,
pos: PositionAdjustment,
coord: CoordinateSystem,
ctx: GeomContext
) {
val quantilesHelper = QuantilesHelper(pos, coord, ctx, drawQuantiles, Aes.Y, Aes.VIOLINWIDTH, Aes.X)
): List<SvgLineElement> {
val quantilesHelper = QuantilesHelper(pos, coord, ctx, quantiles, Aes.X)
val toLocationBoundStart: (DataPointAesthetics) -> DoubleVector = { p ->
DoubleVector(toLocationBound(negativeSign, ctx)(p).x, p.y()!!)
}
val toLocationBoundEnd: (DataPointAesthetics) -> DoubleVector = { p ->
DoubleVector(toLocationBound(positiveSign, ctx)(p).x, p.y()!!)
}
for (line in quantilesHelper.createGroupedQuantiles(dataPoints, toLocationBoundStart, toLocationBoundEnd)) {
root.add(line)
}
return quantilesHelper.getQuantileLineElements(dataPoints, Aes.Y, toLocationBoundStart, toLocationBoundEnd)
}

private fun toLocationBound(
Expand Down Expand Up @@ -134,7 +140,7 @@ class ViolinGeom : GeomBase() {
}

companion object {
val DEF_DRAW_QUANTILES = emptyList<Double>()
const val DEF_QUANTILE_LINES = false
const val DEF_SHOW_HALF = 0.0

const val HANDLES_GROUPS = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import jetbrains.datalore.vis.svg.SvgShape
import jetbrains.datalore.vis.svg.slim.SvgSlimShape

open class GeomHelper(
private val pos: PositionAdjustment,
private val coord: CoordinateSystem,
protected val pos: PositionAdjustment,
protected val coord: CoordinateSystem,
internal val ctx: GeomContext
) {
fun toClient(location: DoubleVector, p: DataPointAesthetics): DoubleVector? {
Expand Down
Loading

0 comments on commit 787c4b5

Please sign in to comment.