我有一个三维数据集,描述了可以表示为图形的基因相互作用。数据集的样本是:
a + b
b + c
c - f
b - d
a + c
f + g
g + h
f + h
'+'表示左侧的基因正向调节右侧的基因。在这个数据中,我想计算一个子图(其中一个基因(比方说,x)正向调节另一个基因(比如y)),y依次正调节另一个基因(比方说,z)。此外,z也受x的正向调节。上图中有两种这样的情况。我想最好使用awk执行此搜索,但任何脚本语言都可以。我为过于具体的问题而道歉,并提前感谢您的帮助。
答案 0 :(得分:2)
注意:请参阅以下有关Graphviz的信息。
这应该给你一个起点:
编辑:此版本处理由多个字符描述的基因。
awk '
BEGIN { regdelim = "|" }
{
delim=""
if ($2 == "+") {
if (plus[$1]) delim=regdelim
plus[$1]=plus[$1] delim $3
}
else
if ($2 == "-") {
if (minus[$1]) delim=regdelim
minus[$1]=minus[$1] delim $3
}
}
END {
for (root in plus) {
split(plus[root],regs,regdelim)
for (reg in regs) {
if (plus[regs[reg]] && plus[root] ~ plus[regs[reg]]) {
print "Match: ", root, "+", regs[reg], "+", plus[regs[reg]]
}
}
}
}
' inputfile
在BEGIN
子句中,将regdelim
设置为未在数据中显示的字符。
我省略了减号数据的处理代码。
输出:
Match: a + b + c
Match: f + g + h
编辑2:
以下版本允许您搜索任意组合。它概括了原始版本中使用的技术,因此不需要复制代码。它还修复了其他几个错误 限制。
#!/bin/bash
# written by Dennis Williamson - 2010-11-12
# for http://stackoverflow.com/questions/4161001/counting-the-occurrence-of-a-sub-graph-in-a-graph
# A (AB) B, A (AC) C, B (BC) C - where "(XY)" represents a + or a -
# provided by the positional parameters $1, $2 and $3
# $4 carries the data file name and is referenced at the end of the script
awk -v AB=$1 -v AC=$2 -v BC=$3 '
BEGIN { regdelim = "|" }
{
if ($2 == AB) {
if (regAB[$1]) delim=regdelim; else delim=""
regAB[$1]=regAB[$1] delim $3
}
if ($2 == AC) {
if (regAC[$1]) delim=regdelim; else delim=""
regAC[$1]=regAC[$1] delim $3
}
if ($2 == BC) {
if (regBC[$1]) delim=regdelim; else delim=""
regBC[$1]=regBC[$1] delim $3
}
}
END {
for (root in regAB) {
split(regAB[root],ABarray,regdelim)
for (ABindex in ABarray) {
split(regAC[root],ACarray,regdelim)
for (ACindex in ACarray) {
split(regBC[ABarray[ABindex]],BCarray,regdelim)
for (BCindex in BCarray) {
if (ACarray[ACindex] == BCarray[BCindex]) {
print " Match:", root, AB, ABarray[ABindex] ",", root, AC, ACarray[ACindex] ",", ABarray[ABindex], BC, BCarray[BCindex]
}
}
}
}
}
}
' "$4"
这可以这样调用来进行详尽的搜索:
for ab in + -; do for ac in + -; do for bc in + -; do echo "Searching: $ab$ac$bc"; ./searchgraph $ab $ac $bc inputfile; done; done; done
对于这些数据:
a - e
a + b
b + c
c - f
m - n
b - d
a + c
b - e
l - n
f + g
b + i
g + h
l + m
f + h
a + i
a - j
k - j
a - k
调用新版本脚本的shell循环的输出如下所示:
Searching: +++
Match: a + b, a + c, b + c
Match: a + b, a + i, b + i
Match: f + g, f + h, g + h
Searching: ++-
Searching: +-+
Searching: +--
Match: l + m, l - n, m - n
Match: a + b, a - e, b - e
Searching: -++
Searching: -+-
Searching: --+
Searching: ---
Match: a - k, a - j, k - j
编辑3:
另一种方法是使用Graphviz。 DOT language可以描述图形,gvpr
是一种“类似AWK”的 1 编程语言,可以分析和操作DOT文件。
根据问题中显示的格式输入数据,您可以使用以下AWK程序将其转换为DOT:
#!/usr/bin/awk -f
BEGIN {
print "digraph G {"
print " size=\"5,5\""
print " ratio=.85"
print " node [fontsize=24 color=blue penwidth=3]"
print " edge [fontsize=18 labeldistance=5 labelangle=-8 minlen=2 penwidth=3]"
print " {rank=same; f l}"
m = "-" # ASCII minus/hyphen as in the source data
um = "−" # u2212 minus: − which looks better on the output graphic
p = "+"
}
{
if ($2 == m) { $2 = um; c = lbf = "red"; arr=" arrowhead = empty" }
if ($2 == p) { c = lbf = "green3"; arr="" }
print " " $1, "->", $3, "[taillabel = \"" $2 "\" color = \"" c "\" labelfontcolor = \"" lbf "\"" arr "]"
}
END {
print "}"
}
要运行的命令是这样的:
$ ./dat2dot data.dat > data.dot
然后,您可以使用以下方法创建上面的图形:
$ dot -Tpng -o data.png data.dot
我在这个答案中使用了上面给出的扩展数据。
要对您指定的子图类型进行详尽搜索,可以使用以下gvpr
程序:
BEGIN {
edge_t AB, BC, AC;
}
E {
AB = $;
BC = fstedge(AB.head);
while (BC && BC.head.name != AB.head.name) {
AC = isEdge(AB.tail,BC.head,"");
if (AC) {
printf("%s %s %s, ", AB.tail.name, AB.taillabel, AB.head.name);
printf("%s %s %s, ", AC.tail.name, AC.taillabel, AC.head.name);
printf("%s %s %s\n", BC.tail.name, BC.taillabel, BC.head.name);
}
BC = nxtedge(BC, AB.head);
}
}
要运行它,您可以使用:
$ gvpr -f groups.g data.dot | sort -k 2,2 -k 5,5 -k 8,8
输出类似于上面AWK / shell组合的输出(在“编辑2”下):
a + b, a + c, b + c
a + b, a + i, b + i
f + g, f + h, g + h
a + b, a − e, b − e
l + m, l − n, m − n
a − k, a − j, k − j
1 松散地说。
答案 1 :(得分:1)
使用Perl的非常规方法如下。
#! /usr/bin/perl
use warnings;
use strict;
my $graph = q{
a + c
b + c
c - f
b - d
a + b
f + g
g + h
f + h
};
my $nodes = join ",", sort keys %{ { map +($_ => 1), $graph =~ /(\w+)/g } };
my $search = "$nodes:$nodes:$nodes:$graph";
my $subgraph = qr/
\A .*? (?<x>\w+) .*:
.*? (?<y>\w+) .*:
.*? (?<z>\w+) .*:
(?= .*^\s* \k<x> \s* \+ \s* \k<y> \s*$)
(?= .*^\s* \k<y> \s* \+ \s* \k<z> \s*$)
(?= .*^\s* \k<x> \s* \+ \s* \k<z> \s*$)
(?{ print "x=$+{x}, y=$+{y}, z=$+{z}\n" })
(?!)
/smx;
$search =~ /$subgraph/;
正则表达式引擎is a powerful tool。对于您的问题,我们描述了传递子图的结构,然后允许生成的机器找到所有这些。
输出:
x=a, y=b, z=c x=f, y=g, z=h
使用相同技术的更通用的工具是可能的。例如,假设您希望能够指定基因模式,例如a+b+c;a+c
或g1+g2-g3;g1+g3
。我希望这些模式的含义是显而易见的。
在前面的内容中,我指定了最低版本5.10.0,因为代码使用//
和词汇$_
。该代码构造了动态正则表达式,用于评估use re 'eval'
pragma启用的代码。
#! /usr/bin/perl
use warnings;
use strict;
use 5.10.0;
use re 'eval';
标识符是一个或多个“单词字符”,即,字母,数字或下划线的序列。
my $ID = qr/\w+/;
给定一个接受变量名的正则表达式,unique_vars
搜索所有变量名称的一些规范并返回它们而不重复。
sub unique_vars {
my($_,$pattern) = @_;
keys %{ { map +($_ => undef), /($pattern)/g } };
}
将基因模式编译成正则表达式有点毛茸茸。它以与上面静态格式相同的形式动态生成搜索目标和正则表达式。
第一部分出现多次逗号分隔变量,让正则表达式引擎尝试每个基因的每个可能值。然后,前瞻(?=...)
扫描图形,查找具有所需属性的边。如果所有前瞻都成功,我们会记录命中。
最后的奇怪的正则表达式(?!)
是无条件的失败,迫使匹配者回溯并尝试与不同基因的匹配。因为它是无条件的,引擎将评估所有可能性。
同时从多个线程调用相同的闭包可能会产生奇怪的结果。
sub compile_gene_pattern {
my($dataset,$pattern) = @_;
my @vars = sort +unique_vars $pattern, qr/[a-z]\d*/; # / for SO hilite
my $nodes = join ",", sort +unique_vars $dataset, $ID;
my $search = join("", map "$_:", ($nodes) x @vars) . "\n"
. $dataset;
my $spec = '\A' . "\n" . join("", map ".*? (?<$_>$ID) .*:\n", @vars);
for (split /;/, $pattern) {
while (s/^($ID)([-+])($ID)/$3/) {
$spec .= '(?= .*^\s* ' .
' \b\k<' . $1 . '>\b ' .
' \s*' . quotemeta($2) . '\s* ' .
' \b\k<' . $3 . '>\b ' .
' \s*$)' . "\n";
}
}
my %hits;
$spec .= '(?{ ++$hits{join "-", @+{@vars}} })' . "\n" .
'(?!) # backtrack' . "\n";
my $nfa = eval { qr/$spec/smx } || die "$0: INTERNAL: bad regex:\n$@";
sub {
%hits = (); # thread-safety? :-(
(my $_ = $search) =~ /$nfa/;
map [split /-/], sort keys %hits;
}
}
阅读数据集,让用户了解任何问题。
sub read_dataset {
my($path) = @_;
open my $fh, "<", $path or die "$0: open $path: $!";
local $/ = "\n";
local $_;
my $graph;
my @errors;
while (<$fh>) {
next if /^\s*#/ || /^\s*$/;
if (/^ \s* $ID \s* [-+] \s* $ID \s* $/x) {
$graph .= $_;
}
else {
push @errors, "$.: syntax error";
}
}
return $graph unless @errors;
die map "$0: $path:$_\n", @errors;
}
现在我们将其全部设置为动作:
my $graphs = shift // "graphs.txt";
my $dataset = read_dataset $graphs;
my $ppp = compile_gene_pattern $dataset, "a+b+c;a+c";
print "@$_\n" for $ppp->();
my $pmp = compile_gene_pattern $dataset, "g1+g2-g3;g1+g3";
print "@$_\n" for $pmp->();
给定graphs.txt
内容
a + b b + c c - f b - d a + c f + g g + h f + h foo + bar bar - baz foo + baz
然后运行程序,我们得到以下输出:
a b c f g h foo bar baz
答案 2 :(得分:0)
我假设“计算子图”是指计算子图中的节点。如果这就是你需要的,你可以使用任何脚本语言,并且必须存储图形,首先,通过创建存储图形的结构或类,节点结构/类应该如下所示(这不符合要求)任何语言的语法,这只是你的应用程序的计划):
Node {color = 0; title = ""; minusNodeSet = null; plusNodeSet = null}
如果color = 0(颜色的默认值意味着您之前没有访问过此节点),则标题将为“a”,“b”,“c”等。 minusNodeSet是一组节点,其中存储了这些节点,其中来自Node的负顶点,plusNodeSet是存储这些节点的一组节点,其中一个正顶点指向我们的节点。
现在,我们有一个架构,应该在深度优先算法中使用它:
int depth_first(Node actualNode)
{
if (actualNode.color == 1)
return;
number = 1;
actualNode.color = 1;
foreach actualNode.nodeSet as node do
if (node.color == 0)
number = number + depth_first(node);
return number;
}
如果我误解了你的问题,请告诉我,能够编辑我的答案,使其更有用。
答案 3 :(得分:0)
my other answer中正则表达式的结构类似于list-monad处理。鉴于这种灵感,对传递子图的搜索低于literate Haskell。将此答案复制并粘贴到扩展名为.lhs
的文件中,以获取正常工作的程序。请务必将带有空行的>
标记的代码部分包围。
感谢有趣的问题!
一些重要事项:
> {-# LANGUAGE ViewPatterns #-}
> module Main where
> import Control.Monad (guard)
> import Data.List (nub)
> import Data.Map (findWithDefault,fromListWith,toList)
基因的名称可以是任何字符串,对于给定的Gene
g ,类型PosReg
的函数应该为我们提供的所有基因g 积极调节。
> type Gene = String
> type PosReg = Gene -> [Gene]
从问题中指定的图表中,我们想要基因的三元组,使得正向调节关系具有传递性,subgraphs
描述所需的属性。首先,从图中选择一个任意的基因 x 。接下来,选择 x 正向调节的 y 基因之一。对于要传递的传递属性, z 必须是 x 和 y 都能正向调节的基因。
> subgraphs :: String -> [(Gene,Gene,Gene)]
> subgraphs g = do
> x <- choose
> y <- posRegBy x
> z <- posRegBy y
> guard $ z `elem` posRegBy x
> return (x,y,z)
> where (choose,posRegBy) = decode g
使用decode
中的简单解析器,我们提取图中的基因列表和PosReg
函数,该函数使所有基因受到其他基因的正调控。
> decode :: String -> ([Gene], PosReg)
> decode g =
> let pr = fromListWith (++) $ go (lines g)
> gs = nub $ concatMap (\(a,b) -> a : b) $ toList pr
> in (gs, (\x -> findWithDefault [] x pr))
> where
> go ((words -> [a, op, b]):ls)
> | op == "+" = (a,[b]) : go ls
> | otherwise = go ls
> go _ = []
最后,主程序将它们粘合在一起。对于找到的每个子图,将其打印到标准输出。
> main :: IO ()
> main = mapM_ (putStrLn . show) $ subgraphs graph
> where graph = "a + b\n\
> \b + c\n\
> \c - f\n\
> \b - d\n\
> \a + c\n\
> \f + g\n\
> \g + h\n\
> \f + h\n"
输出:
("a","b","c") ("f","g","h")