feat(geneset): save diff exp result as geneset (#2216)

* update package lock

* allow falsey vals

* remove old diff exp handling

* save diff exp results as geneset

* delete test

* check for undefined or null

* use global geneset description for diffexp

* remove diffexp special code, no longer showing adjpval + logfoldchange

* remove differential map to state

* remove clear from FSM, since we no longer support those actions

* restore controlHelpers test with todo
This commit is contained in:
Severiano Badajoz
2021-05-25 10:51:31 -07:00
committed by GitHub
parent 6f6634a4d9
commit a4050f10e1
23 changed files with 21894 additions and 355 deletions

View File

@@ -1,38 +1,8 @@
/*
test controls helpers
*/
import { subsetAndResetGeneLists } from "../../../src/util/stateManager/controlsHelpers";
import * as globals from "../../../src/globals";
describe("controls helpers", () => {
test("subsetAndResetGeneLists", () => {
const geneList = [];
const genRandGene = () => Math.random().toString(36).substring(2, 6);
// build a unique set of genes
for (let i = 0; i < 150; i += 1) {
let randGene = genRandGene();
while (geneList.includes(randGene)) randGene = genRandGene();
geneList.push(randGene);
}
// insert duplicates
geneList[0] = "dupl";
geneList[20] = "dupl";
const state = {
userDefinedGenes: geneList.slice(0, 20),
diffexpGenes: geneList.slice(20),
};
const [newUserDefinedGenes, newDiffExpGenes] = subsetAndResetGeneLists(
state
);
const expectedNewUserDefinedGenes = [
...geneList.slice(0, 20),
...geneList.slice(21),
].slice(0, globals.maxGenes);
expect(globals.maxUserDefinedGenes).toBeLessThan(globals.maxGenes);
expect(geneList.length).toBeGreaterThan(globals.maxGenes);
expect(newUserDefinedGenes).toHaveLength(globals.maxGenes);
expect(newUserDefinedGenes).toStrictEqual(expectedNewUserDefinedGenes);
expect(newDiffExpGenes).toStrictEqual([]);
});
// TODO #2227 test: improve test coverage on control helper functions
// (`topNCategories()`, `isSelectableCategoryName()`, `selectableCategoryNames()`, `createCategorySummaryFromDfCol()`, `createCategoricalSelection()`, )
});

21822
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,22 +8,17 @@ const HistogramFooter = React.memo(
rangeMax,
rangeColorMin,
rangeColorMax,
logFoldChange,
pvalAdj,
isObs,
isGeneSetSummary,
}) => {
/*
Footer of each histogram. Will render range, title, and optionally
differential expression info.
Footer of each histogram. Will render range and title.
Required props:
* displayName - the displayName, aka "n_genes", "FOXP2", etc.
* hideRanges - true/false, enables/disable rendering of ranges
* range - length two array, [min, max], containing the range values to display
* rangeColor - length two array, [mincolor, maxcolor], each a CSS color
* logFoldChange - lfc to display, optional.
* pValue - pValue to display, optional.
*/
return (
<div>
@@ -60,30 +55,6 @@ const HistogramFooter = React.memo(
max {rangeMax.toPrecision(4)}
</span>
</div>
{logFoldChange && pvalAdj ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "baseline",
}}
>
<span>
<strong>log fold change:</strong>
{` ${logFoldChange.toPrecision(4)}`}
</span>
<span
style={{
marginLeft: 7,
padding: 2,
}}
>
<strong>p-value (adj):</strong>
{pvalAdj < 0.0001 ? " < 0.0001" : ` ${pvalAdj.toFixed(4)}`}
</span>
</div>
) : null}
</div>
);
}

View File

@@ -14,9 +14,9 @@ import StillLoading from "./loading";
import ErrorLoading from "./error";
@connect((state, ownProps) => {
const { isObs, isUserDefined, isDiffExp, isGeneSetSummary, field } = ownProps;
const { isObs, isUserDefined, isGeneSetSummary, field } = ownProps;
const myName = makeContinuousDimensionName(
{ isObs, isUserDefined, isDiffExp, isGeneSetSummary },
{ isObs, isUserDefined, isGeneSetSummary },
field
);
return {
@@ -80,7 +80,6 @@ class HistogramBrush extends React.PureComponent {
field,
isObs,
isUserDefined,
isDiffExp,
isGeneSetSummary,
} = this.props;
@@ -98,7 +97,6 @@ class HistogramBrush extends React.PureComponent {
continuousNamespace: {
isObs,
isUserDefined,
isDiffExp,
isGeneSetSummary,
},
};
@@ -115,7 +113,6 @@ class HistogramBrush extends React.PureComponent {
field,
isObs,
isUserDefined,
isDiffExp,
isGeneSetSummary,
} = this.props;
const minAllowedBrushSize = 10;
@@ -156,7 +153,6 @@ class HistogramBrush extends React.PureComponent {
continuousNamespace: {
isObs,
isUserDefined,
isDiffExp,
isGeneSetSummary,
},
};
@@ -362,10 +358,7 @@ class HistogramBrush extends React.PureComponent {
field,
isColorAccessor,
isUserDefined,
isDiffExp,
logFoldChange,
isGeneSetSummary,
pvalAdj,
isScatterplotXXaccessor,
isScatterplotYYaccessor,
zebra,
@@ -383,7 +376,7 @@ class HistogramBrush extends React.PureComponent {
heightMini,
} = this.state;
const fieldForId = field.replace(/\s/g, "_");
const showScatterPlot = isDiffExp || isUserDefined;
const showScatterPlot = isUserDefined;
return (
<Async
@@ -406,9 +399,7 @@ class HistogramBrush extends React.PureComponent {
id={`histogram_${fieldForId}`}
data-testid={`histogram-${field}`}
data-testclass={
isDiffExp
? "histogram-diffexp"
: isUserDefined
isUserDefined
? "histogram-user-gene"
: "histogram-continuous-metadata"
}
@@ -454,15 +445,12 @@ class HistogramBrush extends React.PureComponent {
<HistogramFooter
isGeneSetSummary={isGeneSetSummary}
isObs={isObs}
isDiffExp={isDiffExp}
displayName={field}
hideRanges={asyncProps.isSingleValue}
rangeMin={asyncProps.unclippedRange[0]}
rangeMax={asyncProps.unclippedRange[1]}
rangeColorMin={asyncProps.unclippedRangeColor[0]}
rangeColorMax={asyncProps.unclippedRangeColor[1]}
logFoldChange={logFoldChange}
pvalAdj={pvalAdj}
/>
)}
</div>

View File

@@ -45,7 +45,6 @@ const LABEL_WIDTH_ANNO = LABEL_WIDTH - ANNO_BUTTON_WIDTH;
crossfilter: state.obsCrossfilter,
isUserAnno,
genesets: state.genesets.genesets,
differential: state.differential,
};
})
class Category extends React.PureComponent {
@@ -144,15 +143,14 @@ class Category extends React.PureComponent {
*/
const { schema } = annoMatrix;
const { colorAccessor, colorMode } = colors;
const { genesets, differential } = this.props;
const { genesets } = this.props;
let colorDataPromise = Promise.resolve(null);
if (colorAccessor) {
const query = createColorQuery(
colorMode,
colorAccessor,
schema,
genesets,
differential.diffExp
genesets
);
if (query) colorDataPromise = annoMatrix.fetch(...query);
}

View File

@@ -107,11 +107,10 @@ const continuous = (selectorId, colorScale, colorAccessor) => {
annoMatrix: state.annoMatrix,
colors: state.colors,
genesets: state.genesets.genesets,
differential: state.differential,
}))
class ContinuousLegend extends React.Component {
async componentDidUpdate(prevProps) {
const { annoMatrix, colors, genesets, differential } = this.props;
const { annoMatrix, colors, genesets } = this.props;
if (!colors || !annoMatrix) return;
if (colors !== prevProps?.colors || annoMatrix !== prevProps?.annoMatrix) {
@@ -122,8 +121,7 @@ class ContinuousLegend extends React.Component {
colorMode,
colorAccessor,
schema,
genesets,
differential.diffExp
genesets
);
const colorDf = colorQuery ? await annoMatrix.fetch(...colorQuery) : null;

View File

@@ -63,9 +63,6 @@ class Gene extends React.Component {
isColorAccessor,
isScatterplotXXaccessor,
isScatterplotYYaccessor,
isDiffExp,
pvalAdj,
logFoldChange,
} = this.props;
const { geneIsExpanded } = this.state;
const genesetNameLengthVisible = 310; /* this magic number determines how much of a long geneset name we see */
@@ -120,25 +117,20 @@ class Gene extends React.Component {
</Truncate>
</div>
{!geneIsExpanded ? (
isDiffExp ? (
<HistogramBrush isDiffExp field={gene} mini />
) : (
<HistogramBrush isUserDefined field={gene} mini />
)
<HistogramBrush isUserDefined field={gene} mini />
) : null}
</div>
<div style={{ flexShrink: 0, marginLeft: 2 }}>
{!isDiffExp ? (
<AnchorButton
minimal
small
data-testid={`delete-from-geneset-${gene}`}
onClick={this.handleDeleteGeneFromSet}
intent="none"
style={{ fontWeight: 700, marginRight: 2 }}
icon={<Icon icon="trash" iconSize={10} />}
/>
) : null}
<AnchorButton
minimal
small
data-testid={`delete-from-geneset-${gene}`}
onClick={this.handleDeleteGeneFromSet}
intent="none"
style={{ fontWeight: 700, marginRight: 2 }}
icon={<Icon icon="trash" iconSize={10} />}
/>
)
<AnchorButton
minimal
small
@@ -184,17 +176,7 @@ class Gene extends React.Component {
/>
</div>
</div>
{geneIsExpanded &&
(isDiffExp ? (
<HistogramBrush
isDiffExp
field={gene}
pvalAdj={pvalAdj}
logFoldChange={logFoldChange}
/>
) : (
<HistogramBrush isUserDefined field={gene} />
))}
{geneIsExpanded && <HistogramBrush isUserDefined field={gene} />}
</div>
);
}

View File

@@ -79,33 +79,8 @@ class GeneSet extends React.Component {
};
renderGenes() {
const {
setName,
setGenes,
setGenesWithDescriptions,
isDiffExp,
diffExp,
} = this.props;
const { setName, setGenes, setGenesWithDescriptions } = this.props;
if (isDiffExp) {
// [ [gene, logfoldchange, pval, pval_adj], ...]
return setGenes.map((gene, idx) => {
const logFoldChange = diffExp[idx][1];
const pvalAdj = diffExp[idx][3];
return (
<Gene
key={gene}
gene={gene}
geneset={setName}
isDiffExp
logFoldChange={logFoldChange}
pvalAdj={pvalAdj}
/>
);
});
}
// otherwise...
return setGenes.map((gene) => {
const { geneDescription } = setGenesWithDescriptions.get(gene);

View File

@@ -7,7 +7,6 @@ import CreateGenesetDialogue from "./menus/createGenesetDialogue";
@connect((state) => {
return {
differential: state.differential,
genesets: state.genesets.genesets,
};
})
@@ -30,24 +29,6 @@ class GeneExpression extends React.Component {
return sets;
};
renderDiffexpGeneSets = () => {
const { differential } = this.props;
const { diffExp } = differential;
if (!diffExp) return null;
// [ [gene, logfoldchange, pval, pval_adj], ...]
const setGenes = diffExp.map((diffExpGene) => diffExpGene[0]);
return (
<GeneSet
key="Temp DiffExp Set"
setGenes={setGenes}
isDiffExp
diffExp={diffExp}
setName="Temp DiffExp Set"
/>
);
};
handleActivateCreateGenesetMode = () => {
const { dispatch } = this.props;
dispatch({ type: "geneset: activate add new geneset mode" });
@@ -68,7 +49,6 @@ class GeneExpression extends React.Component {
</div>
<CreateGenesetDialogue />
</div>
<div>{this.renderDiffexpGeneSets()}</div>
<div>{this.renderGeneSets()}</div>
</div>
);

View File

@@ -54,7 +54,6 @@ const filterGenes = (query, genes) =>
annoMatrix: state.annoMatrix,
userDefinedGenes: state.controls.userDefinedGenes,
userDefinedGenesLoading: state.controls.userDefinedGenesLoading,
differential: state.differential,
};
})
class AddGenes extends React.Component {

View File

@@ -77,7 +77,6 @@ function createModelTF() {
colors: state.colors,
pointDilation: state.pointDilation,
genesets: state.genesets.genesets,
differential: state.differential,
}))
class Graph extends React.Component {
static createReglState(canvas) {
@@ -785,17 +784,11 @@ class Graph extends React.Component {
}
createColorByQuery(colors) {
const { annoMatrix, genesets, differential } = this.props;
const { annoMatrix, genesets } = this.props;
const { schema } = annoMatrix;
const { colorMode, colorAccessor } = colors;
return createColorQuery(
colorMode,
colorAccessor,
schema,
genesets,
differential.diffExp
);
return createColorQuery(colorMode, colorAccessor, schema, genesets);
}
renderPoints(

View File

@@ -15,7 +15,6 @@ export default
categoricalSelection: state.categoricalSelection,
showLabels: state.centroidLabels?.showLabels,
genesets: state.genesets.genesets,
differential: state.differential,
}))
class CentroidLabels extends PureComponent {
static watchAsync(props, prevProps) {
@@ -76,16 +75,10 @@ class CentroidLabels extends PureComponent {
};
colorByQuery() {
const { annoMatrix, colors, genesets, differential } = this.props;
const { annoMatrix, colors, genesets } = this.props;
const { schema } = annoMatrix;
const { colorMode, colorAccessor } = colors;
return createColorQuery(
colorMode,
colorAccessor,
schema,
genesets,
differential.diffExp
);
return createColorQuery(colorMode, colorAccessor, schema, genesets);
}
async fetchData() {

View File

@@ -9,12 +9,9 @@ import actions from "../../actions";
}))
class CellSetButton extends React.PureComponent {
set() {
const { differential, dispatch, eitherCellSetOneOrTwo } = this.props;
const { dispatch, eitherCellSetOneOrTwo } = this.props;
if (!differential.diffExp) {
// disallow this action if the user has active differential expression results
dispatch(actions.setCellSetFromSelection(eitherCellSetOneOrTwo));
}
dispatch(actions.setCellSetFromSelection(eitherCellSetOneOrTwo));
}
render() {
@@ -31,7 +28,6 @@ class CellSetButton extends React.PureComponent {
>
<AnchorButton
type="button"
disabled={differential.diffExp}
onClick={() => {
this.set();
}}

View File

@@ -1,6 +1,6 @@
import React from "react";
import { connect } from "react-redux";
import { Button, ButtonGroup, AnchorButton, Tooltip } from "@blueprintjs/core";
import { ButtonGroup, AnchorButton, Tooltip } from "@blueprintjs/core";
import * as globals from "../../globals";
import styles from "./menubar.css";
import actions from "../../actions";
@@ -8,8 +8,6 @@ import CellSetButton from "./cellSetButtons";
@connect((state) => ({
differential: state.differential,
celllist1: state.differential?.celllist1,
celllist2: state.differential?.celllist2,
diffexpMayBeSlow: state.config?.parameters?.["diffexp-may-be-slow"] ?? false,
diffexpCellcountMax: state.config?.limits?.diffexp_cellcount_max,
}))
@@ -26,17 +24,6 @@ class DiffexpButtons extends React.PureComponent {
}
};
clearDifferentialExpression = () => {
const { dispatch, differential } = this.props;
dispatch({
type: "clear differential expression",
diffExp: differential.diffExp,
});
dispatch({
type: "clear scatterplot",
});
};
render() {
/* diffexp-related buttons may be disabled */
const { differential, diffexpMayBeSlow, diffexpCellcountMax } = this.props;
@@ -65,41 +52,22 @@ class DiffexpButtons extends React.PureComponent {
<ButtonGroup className={styles.menubarButton}>
<CellSetButton eitherCellSetOneOrTwo={1} />
<CellSetButton eitherCellSetOneOrTwo={2} />
{!differential.diffExp ? (
<Tooltip
content={warnMaxSizeExceeded ? tipMessageWarn : tipMessage}
position="bottom"
hoverOpenDelay={globals.tooltipHoverOpenDelayQuick}
intent={warnMaxSizeExceeded ? "danger" : "none"}
>
<AnchorButton
disabled={!haveBothCellSets || warnMaxSizeExceeded}
intent={warnMaxSizeExceeded ? "danger" : "primary"}
data-testid="diffexp-button"
loading={differential.loading}
icon="left-join"
fill
onClick={this.computeDiffExp}
/>
</Tooltip>
) : null}
{differential.diffExp ? (
<Tooltip
content="Remove differentially expressed gene list and clear cell selections"
position="bottom"
hoverOpenDelay={globals.tooltipHoverOpenDelayQuick}
>
<Button
type="button"
fill
intent="warning"
onClick={this.clearDifferentialExpression}
>
Clear Differential Expression
</Button>
</Tooltip>
) : null}
<Tooltip
content={warnMaxSizeExceeded ? tipMessageWarn : tipMessage}
position="bottom"
hoverOpenDelay={globals.tooltipHoverOpenDelayQuick}
intent={warnMaxSizeExceeded ? "danger" : "none"}
>
<AnchorButton
disabled={!haveBothCellSets || warnMaxSizeExceeded}
intent={warnMaxSizeExceeded ? "danger" : "primary"}
data-testid="diffexp-button"
loading={differential.loading}
icon="left-join"
fill
onClick={this.computeDiffExp}
/>
</Tooltip>
</ButtonGroup>
);
}

View File

@@ -29,17 +29,13 @@ import { getEmbSubsetView } from "../../util/stateManager/viewStackHelpers";
return {
subsetPossible,
subsetResetPossible,
differential: state.differential,
graphInteractionMode: state.controls.graphInteractionMode,
clipPercentileMin: Math.round(100 * (annoMatrix?.clipRange?.[0] ?? 0)),
clipPercentileMax: Math.round(100 * (annoMatrix?.clipRange?.[1] ?? 1)),
userDefinedGenes: state.controls.userDefinedGenes,
diffexpGenes: state.controls.diffexpGenes,
colorAccessor: state.colors.colorAccessor,
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
scatterplotYYaccessor: state.controls.scatterplotYYaccessor,
celllist1: state.differential.celllist1,
celllist2: state.differential.celllist2,
libraryVersions: state.config?.library_versions,
auth: state.config?.authentication,
userInfo: state.userInfo,

View File

@@ -51,7 +51,6 @@ const getYScale = memoize(getScale);
scatterplotXXaccessor,
scatterplotYYaccessor,
differential: state.differential,
crossfilter,
genesets: state.genesets.genesets,
};
@@ -314,16 +313,10 @@ class Scatterplot extends React.PureComponent {
}
createColorByQuery(colors) {
const { annoMatrix, genesets, differential } = this.props;
const { annoMatrix, genesets } = this.props;
const { schema } = annoMatrix;
const { colorMode, colorAccessor } = colors;
return createColorQuery(
colorMode,
colorAccessor,
schema,
genesets,
differential
);
return createColorQuery(colorMode, colorAccessor, schema, genesets);
}
updateColorTable(colors, colorDf) {

View File

@@ -7,9 +7,7 @@ const ColorsReducer = (
colorMode: null /* by continuous, by expression */,
colorAccessor: null /* tissue, Apod */,
},
action,
nextSharedState,
prevSharedState
action
) => {
switch (action.type) {
case "universe: user color load success": {
@@ -20,19 +18,6 @@ const ColorsReducer = (
};
}
case "clear differential expression":
case "set clip quantiles":
case "subset to selection": {
const { controls: prevControls } = prevSharedState;
if (prevControls.diffexpGenes.includes(state.colorAccessor)) {
return {
colorMode: null,
colorAccessor: null,
};
}
return state;
}
case "annotation: category edited": {
const { colorAccessor } = state;
if (action.metadataField !== colorAccessor) {

View File

@@ -1,6 +1,5 @@
import uniq from "lodash.uniq";
import filter from "lodash.filter";
import { subsetAndResetGeneLists } from "../util/stateManager/controlsHelpers";
const Controls = (
state = {
@@ -11,7 +10,6 @@ const Controls = (
// all of the data + selection state
userDefinedGenes: [],
userDefinedGenesLoading: false,
diffexpGenes: [],
resettingInterface: false,
graphInteractionMode: "select",
@@ -45,26 +43,16 @@ const Controls = (
};
}
case "reset subset": {
const [newUserDefinedGenes, newDiffExpGenes] = subsetAndResetGeneLists(
state
);
return {
...state,
resettingInterface: false,
userDefinedGenes: newUserDefinedGenes,
diffexpGenes: newDiffExpGenes,
};
}
case "subset to selection": {
const [newUserDefinedGenes, newDiffExpGenes] = subsetAndResetGeneLists(
state
);
return {
...state,
loading: false,
error: null,
userDefinedGenes: newUserDefinedGenes,
diffexpGenes: newDiffExpGenes,
};
}
case "request user defined gene started": {
@@ -90,19 +78,6 @@ const Controls = (
userDefinedGenesLoading: false,
};
}
case "request differential expression success": {
const diffexpGenes = action.data.map((v) => v[0]);
return {
...state,
diffexpGenes,
};
}
case "clear differential expression": {
return {
...state,
diffexpGenes: [],
};
}
case "clear user defined gene": {
const { userDefinedGenes } = state;
const newUserDefinedGenes = filter(

View File

@@ -1,6 +1,5 @@
const Differential = (
state = {
diffExp: null,
loading: null,
error: null,
celllist1: null,
@@ -20,7 +19,6 @@ const Differential = (
...state,
error: null,
loading: false,
diffExp: action.data,
};
case "request differential expression error":
return {
@@ -38,18 +36,10 @@ const Differential = (
...state,
celllist2: action.data,
};
case "clear differential expression":
return {
...state,
diffExp: null,
celllist1: null,
celllist2: null,
};
case "reset subset":
case "subset to selection":
return {
...state,
diffExp: null,
celllist1: null,
celllist2: null,
};

View File

@@ -69,6 +69,7 @@ const GeneSets = (
}
return {
...state,
initialized: true,
lastTid,
genesets,
@@ -353,6 +354,33 @@ const GeneSets = (
};
}
case "request differential expression success": {
const { data } = action;
const genes = new Map(
data.map((diffExpGene) => [
diffExpGene[0],
{
geneSymbol: diffExpGene[0],
},
])
);
const genesetName = `DiffExp Set (${new Date().toLocaleString()})`;
const genesets = new Map(state.genesets); // clone
genesets.set(genesetName, {
genesetName,
genesetDescription: "",
genes,
});
return {
...state,
genesets,
};
}
default:
return state;
}

View File

@@ -185,20 +185,6 @@ const createFsmTransitions = (
action: applyPending,
},
/* Clear Differential Expression button user action */
{
event: "clear differential expression",
from: "init",
to: "CDE Button in progress",
action: stashPending,
},
{
event: "clear scatterplot",
from: "CDE Button in progress",
to: "done",
action: applyPending,
},
/* clear scatter plot button (eg, on scatterplot view) */
{
event: "clear scatterplot",

View File

@@ -12,13 +12,7 @@ import { range } from "../range";
given a color mode & accessor, generate an annoMatrix query that will
fulfill it
*/
export function createColorQuery(
colorMode,
colorByAccessor,
schema,
genesets,
diffExp
) {
export function createColorQuery(colorMode, colorByAccessor, schema, genesets) {
if (!colorMode || !colorByAccessor || !schema || !genesets) return null;
switch (colorMode) {
@@ -46,19 +40,8 @@ export function createColorQuery(
if (!varIndex) return null;
if (!genesets) return null;
let _geneset;
let _setGenes;
if (colorByAccessor === "Temp DiffExp Set") {
_geneset = diffExp;
_setGenes = _geneset.map((diffexpResultItem) => {
const geneName = diffexpResultItem[0];
return geneName;
});
} else {
_geneset = genesets.get(colorByAccessor);
_setGenes = [..._geneset.genes.keys()];
}
const _geneset = genesets.get(colorByAccessor);
const _setGenes = [..._geneset.genes.keys()];
return [
"X",

View File

@@ -2,7 +2,6 @@
Helper functions for the controls reducer
*/
import uniq from "lodash.uniq";
import difference from "lodash.difference";
import * as globals from "../../globals";
@@ -158,12 +157,3 @@ export function pruneVarDataCache(varData, needed) {
}
return varData;
}
export function subsetAndResetGeneLists(state) {
const { userDefinedGenes, diffexpGenes } = state;
const newUserDefinedGenes = uniq(
[].concat(userDefinedGenes, diffexpGenes)
).slice(0, globals.maxGenes);
const newDiffExpGenes = [];
return [newUserDefinedGenes, newDiffExpGenes];
}