birch-beer 0.1.0.1 → 0.1.1.0
raw patch · 7 files changed
+68/−7 lines, 7 files
Files
- app/Main.hs +14/−3
- birch-beer.cabal +4/−2
- src/BirchBeer/Interactive.hs +2/−0
- src/BirchBeer/MainDiagram.hs +8/−1
- src/BirchBeer/Stopping.hs +11/−0
- src/BirchBeer/Types.hs +4/−0
- src/BirchBeer/Utility.hs +25/−1
app/Main.hs view
@@ -24,7 +24,9 @@ import TextShow (showt) import qualified Control.Lens as L import qualified Data.Aeson as A+import qualified Data.ByteString.Lazy.Char8 as B import qualified Data.Csv as CSV+import qualified Data.Set as Set import qualified Data.Sparse.Common as S import qualified Data.Text as T import qualified Data.Vector as V@@ -45,6 +47,7 @@ { input :: String <?> "(FILE) The input JSON file." , inputMatrix :: Maybe String <?> "([Nothing] | FILE) The input adjacency matrix file for CollectionGraph (matrix market format if ends in .mtx, \"i,j,value\" without header otherwise and text labels will be sorted when converting indices)." , output :: Maybe String <?> "([dendrogram.svg] | FILE) The filename for the dendrogram. Supported formats are PNG, PS, PDF, and SVG."+ , jsonOutput :: Maybe String <?> "([Nothing] | FILE) The filename for the output json tree. The input tree can change based on pruning, so this option provides a way to output the new tree as a json." , delimiter :: Maybe Char <?> "([,] | CHAR) The delimiter for csv files." , labelsFile :: Maybe String <?> "([Nothing] | FILE) The input file containing the label for each item, with \"item,label\" header." , minSize :: Maybe Int <?> "([1] | INT) The minimum size of a cluster. Defaults to 1."@@ -52,9 +55,10 @@ , maxProportion :: Maybe Double <?> "([Nothing] | DOUBLE) Stopping criteria to stop at the node immediate after a node with DOUBLE proportion split. So a node N with L and R children will stop with this criteria at 0.5 if |L| / |R| < 0.5 or > 2 (absolute log2 transformed), that is, if one child has over twice as many items as the other child. Includes L and R in the final result." , minDistance :: Maybe Double <?> "([Nothing] | DOUBLE) Stopping criteria to stop at the node immediate after a node with DOUBLE distance. So a node N with L and R children will stop with this criteria the distance at N to L and R is < DOUBLE. Includes L and R in the final result." , smartCutoff :: Maybe Double <?> "([Nothing] | DOUBLE) Whether to set the cutoffs for --min-size, --max-proportion, and --min-distance based off of the distributions (median + (DOUBLE * MAD)) of all nodes. To use smart cutoffs, use this argument and then set one of the three arguments to an arbitrary number, whichever cutoff type you want to use. --min-size distribution is log2 transformed."+ , customCut :: [Int] <?> "([Nothing] | NODE) List of nodes to prune (make these nodes leaves). Invoked by --custom-cut 34 --custom-cut 65 etc." , order :: Maybe Double <?> "([1] | DOUBLE) The order of diversity for DrawItem DrawDiversity." , drawLeaf :: Maybe String <?> "([DrawText] | DrawItem DrawItemType) How to draw leaves in the dendrogram. DrawText is the number of items in that leaf. DrawItem is the collection of items represented by circles, consisting of: DrawItem DrawLabel, where each item is colored by its label, DrawItem (DrawContinuous FEATURE), where each item is colored by the expression of FEATURE (corresponding to a feature name in the input matrix), DrawItem (DrawThresholdContinuous [(FEATURE, DOUBLE)]), where each item is colored by the binary high / low expression of FEATURE based on DOUBLE and multiple FEATUREs can be used to combinatorically label items (FEATURE1 high / FEATURE2 low, etc.), DrawItem DrawSumContinuous, where each item is colored by the sum of the post-normalized columns (use --normalization NoneNorm for UMI counts, default), and DrawItem DrawDiversity, where each node is colored by the diversity based on the labels of each item and the color is normalized separately for the leaves and the inner nodes. The default is DrawText, unless --labels-file is provided, in which DrawItem DrawLabel is the default. If the label or feature cannot be found, the default color will be black (check your spelling!)."- , drawCollection :: Maybe String <?> "([PieChart] | PieRing | PieNone | CollectionGraph MAXWEIGHT THRESHOLD [NODE]) How to draw item leaves in the dendrogram. PieRing draws a pie chart ring around the items. PieChart only draws a pie chart instead of items. PieNone only draws items, no pie rings or charts. (CollectionGraph MAXWEIGHT THRESHOLD [NODE]) draws the nodes and edges within leaves that are descendents of NODE (empty list [] indicates draw all leaf networks) based on the input matrix, normalizes edges based on the MAXWEIGHT, and removes edges for display less than THRESHOLD (after normalization, so for CollectionGraph 2 0.5 [26], draw the leaf graphs for all leaves under node 26, then a edge of 0.7 would be removed because (0.7 / 2) < 0.5)."+ , drawCollection :: Maybe String <?> "([PieChart] | PieRing | PieNone | CollectionGraph MAXWEIGHT THRESHOLD [NODE]) How to draw item leaves in the dendrogram. PieRing draws a pie chart ring around the items. PieChart only draws a pie chart instead of items. PieNone only draws items, no pie rings or charts. (CollectionGraph MAXWEIGHT THRESHOLD [NODE]) draws the nodes and edges within leaves that are descendents of NODE (empty list [] indicates draw all leaf networks) based on the input matrix, normalizes edges based on the MAXWEIGHT, and removes edges for display less than THRESHOLD (after normalization, so for CollectionGraph 2 0.5 [26], draw the leaf graphs for all leaves under node 26, then a edge of 0.7 would be removed because (0.7 / 2) < 0.5). For CollectionGraph with no colors, use --draw-leaf \"DrawItem DrawLabel\" and all nodes will be black. If you don't specify this option, DrawText from --draw-leaf overrides this argument and only the number of cells will be plotted." , drawMark :: Maybe String <?> "([MarkNone] | MarkModularity) How to draw annotations around each inner node in the tree. MarkNone draws nothing and MarkModularity draws a black circle representing the modularity at that node, darker black means higher modularity for that next split." , drawNodeNumber :: Bool <?> "Draw the node numbers on top of each node in the graph." , drawMaxNodeSize :: Maybe Double <?> "([72] | DOUBLE) The max node size when drawing the graph. 36 is the theoretical default, but here 72 makes for thicker branches."@@ -63,7 +67,7 @@ , drawLegendSep :: Maybe Double <?> "([1] | DOUBLE) The amount of space between the legend and the tree." , drawLegendAllLabels :: Bool <?> "Whether to show all the labels in the label file instead of only showing labels within the current tree. The program generates colors from all labels in the label file first in order to keep consistent colors. By default, this value is false, meaning that only the labels present in the tree are shown (even though the colors are the same). The subset process occurs after --draw-colors, so when using that argument make sure to account for all labels." , drawPalette :: Maybe String <?> "([Set1] | Hsv | Ryb) Palette to use for legend colors. With high saturation in --draw-scale-saturation, consider using Hsv to better differentiate colors."- , drawColors :: Maybe String <?> "([Nothing] | COLORS) Custom colors for the labels or continuous features. Will repeat if more labels than provided colors. For continuous feature plots, uses first two colors [high, low], defaults to [red, white]. For instance: --draw-colors \"[\\\"#e41a1c\\\", \\\"#377eb8\\\"]\""+ , drawColors :: Maybe String <?> "([Nothing] | COLORS) Custom colors for the labels or continuous features. Will repeat if more labels than provided colors. For continuous feature plots, uses first two colors [high, low], defaults to [red, gray]. For instance: --draw-colors \"[\\\"#e41a1c\\\", \\\"#377eb8\\\"]\"" , drawScaleSaturation :: Maybe Double <?> "([Nothing] | DOUBLE) Multiply the saturation value all nodes by this number in the HSV model. Useful for seeing more visibly the continuous colors by making the colors deeper against a gray scale." , interactive :: Bool <?> "Display interactive tree." } deriving (Generic)@@ -71,6 +75,7 @@ modifiers :: Modifiers modifiers = lispCaseModifiers { shortNameModifier = short } where+ short "customCut" = Nothing short "inputMatrix" = Just 'X' short "minSize" = Just 'M' short "maxStep" = Just 'S'@@ -111,6 +116,7 @@ maxProportion' = fmap MaxProportion . unHelpful . maxProportion $ opts minDistance' = fmap MinDistance . unHelpful . minDistance $ opts smartCutoff' = fmap SmartCutoff . unHelpful . smartCutoff $ opts+ customCut' = CustomCut . Set.fromList . unHelpful . customCut $ opts order' = fmap Order . unHelpful . order $ opts drawLeaf' = maybe@@ -210,6 +216,7 @@ , _birchMaxProportion = maxProportion' , _birchMinDistance = minDistance' , _birchSmartCutoff = smartCutoff'+ , _birchCustomCut = customCut' , _birchOrder = order' , _birchDrawLeaf = drawLeaf' , _birchDrawCollection = drawCollection'@@ -228,12 +235,16 @@ , _birchSimMat = simMat } - (plot, _, _, _, _, _) <- mainDiagram config+ (plot, _, _, _, tree', _) <- mainDiagram config + -- Plot tree. D.renderCairo output' (D.mkHeight 1000) plot++ -- Write new tree if necessary.+ mapM_ (\x -> B.writeFile x . A.encode $ tree') . unHelpful . jsonOutput $ opts when (unHelpful . interactive $ opts) $ interactiveDiagram tree labelMap (Nothing :: Maybe NamedMatrix) simMat
birch-beer.cabal view
@@ -1,9 +1,9 @@ cabal-version: >=1.10 name: birch-beer-version: 0.1.0.1+version: 0.1.1.0 license: GPL-3 license-file: LICENSE-copyright: 2018 Gregory W. Schwartz+copyright: 2019 Gregory W. Schwartz maintainer: gsch@pennmedicine.upenn.edu author: Gregory W. Schwartz homepage: http://github.com/GregorySchwartz/birch-beer#readme@@ -78,8 +78,10 @@ base >=4.11.1.0, aeson >=1.3.1.1, birch-beer -any,+ bytestring >=0.10.8.2, cassava >=0.5.1.0, colour >=2.3.4,+ containers >=0.5.11.0, diagrams >=1.4, diagrams-cairo >=1.4.1, diagrams-lib >=1.4.2.3,
src/BirchBeer/Interactive.hs view
@@ -23,6 +23,7 @@ import System.IO.Temp (withTempFile) import qualified Control.Lens as L import qualified Data.Clustering.Hierarchical as HC+import qualified Data.Set as Set import qualified Data.Text as T import qualified Data.Vector as V import qualified Diagrams.Backend.Cairo as D@@ -102,6 +103,7 @@ , _birchMaxProportion = Nothing , _birchMinDistance = Nothing , _birchSmartCutoff = Nothing+ , _birchCustomCut = CustomCut Set.empty , _birchOrder = Nothing , _birchDrawLeaf = drawLeaf' , _birchDrawCollection = drawCollection'
src/BirchBeer/MainDiagram.hs view
@@ -42,6 +42,7 @@ let tree = _birchTree config labelMap' = _birchLabelMap config smartCutoff' = _birchSmartCutoff config+ customCut' = _birchCustomCut config maxStep' = _birchMaxStep config minSize' = case (fmap unSmartCutoff smartCutoff', _birchMinSize config) of@@ -83,7 +84,13 @@ -- Prune tree. tree' = foldl' (\acc f -> f acc) tree- $ [ (\x -> maybe x (flip proportionCut x . unMaxProportion) maxProportion')+ $ [ (\x -> bool x (flip clusterGraphToTree 0 . flip customCut (treeToGraph x) . unCustomCut $ customCut')+ . not+ . Set.null+ . unCustomCut+ $ customCut'+ )+ , (\x -> maybe x (flip proportionCut x . unMaxProportion) maxProportion') , (\x -> maybe x (flip distanceCut x . unMinDistance) minDistance') , (\x -> maybe x (flip sizeCut x . unMinClusterSize) minSize') , (\x -> maybe x (flip stepCut x . unMaxStep) maxStep')
src/BirchBeer/Stopping.hs view
@@ -25,6 +25,7 @@ , getProportionDend , smartCut , smartCutDend+ , customCut ) where -- Remote@@ -159,6 +160,16 @@ if (L.view distance . rootLabel $ b) < Just d then b { subForest = fmap branchToLeaf . subForest $ b } else b { subForest = fmap (distanceCut d) . subForest $ b }++-- | Cut a graph tree at designated nodes. Using a graph due to node labels.+-- Must not include cycles!+customCut :: Set.Set G.Node -> ClusterGraph a -> ClusterGraph a+customCut ns gr = ClusterGraph . G.gmap pruneNode . unClusterGraph $ gr+ where+ pruneNode all@(parents, n, a, children)+ | Set.member n ns =+ (parents, n, (n, Just $ getGraphLeafItems gr n), [])+ | otherwise = all -- | Get a property about each node in a dendrogram. getNodeInfoDend :: (HC.Dendrogram a -> b) -> HC.Dendrogram a -> [b]
src/BirchBeer/Types.hs view
@@ -100,6 +100,9 @@ newtype MinDistance = MinDistance { unMinDistance :: Double } deriving (Read,Show)+newtype CustomCut = CustomCut+ { unCustomCut :: Set.Set Int+ } deriving (Read,Show) newtype SmartCutoff = SmartCutoff { unSmartCutoff :: Double } deriving (Read,Show)@@ -201,6 +204,7 @@ data Config a b = Config { _birchLabelMap :: Maybe LabelMap , _birchSmartCutoff :: Maybe SmartCutoff+ , _birchCustomCut :: CustomCut , _birchMinSize :: Maybe MinClusterSize , _birchMaxStep :: Maybe MaxStep , _birchMaxProportion :: Maybe MaxProportion
src/BirchBeer/Utility.hs view
@@ -20,6 +20,7 @@ , dendToTree , dendrogramToGraph , treeToGraph+ , clusterGraphToTree , getGraphLeaves , getGraphLeavesCycles , getGraphLeavesWithParents@@ -41,7 +42,7 @@ import Data.Maybe (fromMaybe, catMaybes) import Data.Monoid ((<>)) import Data.Tree (Tree (..), flatten)-import Safe (atMay)+import Safe (atMay, headMay) import qualified Control.Lens as L import qualified Data.Clustering.Hierarchical as HC import qualified Data.Foldable as F@@ -196,6 +197,29 @@ modify (L.over L._2 setGr) return n++-- | Convert a ClusterGraph to a tree, feed in the starting node. Must not+-- include cycles!+clusterGraphToTree+ :: (TreeItem a) => ClusterGraph a -> G.Node -> Tree (TreeNode (V.Vector a))+clusterGraphToTree (ClusterGraph gr) n | null $ G.suc gr n =+ Node { rootLabel =+ TreeNode { _distance = Nothing+ , _item = fmap (V.fromList . F.toList)+ . join+ . fmap snd+ . G.lab gr+ $ n+ }+ , subForest = []+ }+clusterGraphToTree (ClusterGraph gr) n | otherwise =+ Node { rootLabel =+ TreeNode { _distance = fmap (L.view L._3) . headMay . G.out gr $ n+ , _item = Nothing+ }+ , subForest = fmap (clusterGraphToTree (ClusterGraph gr)) . G.suc gr $ n+ } -- | Get leaves of a tree graph given a node. Graph must not include cycles! getGraphLeaves :: G.Graph gr => gr a b -> G.Node -> Seq.Seq a