XSLT-сортировка групп узлов, ранее выбранных с помощью ключа

У меня есть файл XML с группой узлов protein, каждый из которых имеет номер accession. И группа из peptides каждого из них с параметрами accession, sequence, RT и score.

И у меня есть XSLT-файл, в котором для каждого белка вызываются все пептидные узлы с одинаковым accession номером белка.

Затем, в зависимости от значения переменной, здесь <xsl:param name="analysis" select="0"/>, файл XSLT вызывает все пептиды, которые имеют одинаковый номер accession, или все пептиды, которые имеют один и тот же номер accession, но отбрасывают все те, которые имеют совпадающие значения sequence.

Вот код, который делает то, что я сказал (изменив значение переменной с 0 на 1, можно увидеть 2 ситуации, которые я описал)

ссылка

Я также вставляю код в конце поста

Теперь мне нужно отсортировать и выбрать пептидные узлы с максимальным количеством баллов.

Таким образом, в случае, когда файл XSLT вызывает все пептиды, которые имеют один и тот же номер accession, мне нужно, чтобы они были отсортированы в зависимости от их значений оценки.

И в случае, когда код вызывает все пептиды с одинаковым числом accession, но выбирает только один пептид из тех, которые также имеют одинаковые последовательности, мне нужно, чтобы этот пептид был максимальным, а не только первым. входит в файл XML.

Я попытался использовать функцию "сортировки" в этом коде ссылка, но если вы возьмете взглянув на это, вы увидите, что выходные данные XML упорядочивают все пептиды, теряя предыдущую предварительную группировку, выполненную с помощью оператора key.

XML-код

<data>
    <proteins>
        <protein>
            <accession>111</accession>
        </protein>
    </proteins>
    <peptides>
        <peptide>
            <accession>111</accession>
            <sequence>AAA</sequence>
            <RT>13</RT>
            <score>4000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>AAA</sequence>
            <RT>14</RT>
            <score>6000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>AAA</sequence>
            <RT>15</RT>
            <score>5000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>BBB</sequence>
            <RT>23</RT>
            <score>5000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>BBB</sequence>
            <RT>24</RT>
            <score>1000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>BBB</sequence>
            <RT>25</RT>
            <score>8000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>BBB</sequence>
            <RT>26</RT>
            <score>5000</score>
        </peptide>
    </peptides>
</data>

XSLT-код

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:param name="analysis" select="0"/>
    <xsl:key name="byAcc"    match="/data/peptides/peptide" use="accession" />
    <xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>
    <xsl:template match="/">
        <root>
            <name>
                <xsl:value-of select="$analysis"/>
            </name>
            <xsl:apply-templates select="/data/proteins/protein" />
        </root>
    </xsl:template>
    <xsl:template match="/data/proteins/protein">
        <xsl:choose>
            <xsl:when test="$analysis=1">
                <xsl:apply-templates select="key('byAcc',accession)">
                </xsl:apply-templates>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="key('byAcc',accession)[
                generate-id()
                =
                generate-id(key('byAccSeq', concat(accession, '|', sequence)))]">
            </xsl:apply-templates>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
<xsl:template match="/data/peptides/peptide">
    <xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>

И вывод XML, который мне нужен, для простоты, когда выбран только один пептид <xsl:param name="analysis" select="0"/>

<root>
    <name>0</name>
    <peptide>
        <accession>111</accession>
        <sequence>AAA</sequence>
        <RT>14</RT>
        <score>6000</score>
    </peptide>
    <peptide>
        <accession>111</accession>
        <sequence>BBB</sequence>
        <RT>25</RT>
        <score>8000</score>
    </peptide>
</root>

Это из двух пептидных узлов, которые имеют общие значения accession и sequence, те, у которых максимальное значение score

Спасибо

---------------------------------------------------------------------------

РЕДАКТИРОВАТЬ: Попытка сделать вопрос более ясным

Самый упрощенный код, который я могу придумать, будет таким:

У меня есть этот XML-код

<data>
    <peptides>
        <peptide>
            <accession>111</accession>
            <sequence>AAA</sequence>
            <score>4000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>AAA</sequence>
            <score>6000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>AAA</sequence>
            <score>5000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>BBB</sequence>
            <score>5000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>BBB</sequence>
            <score>1000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>BBB</sequence>
            <score>8000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>BBB</sequence>
            <score>5000</score>
        </peptide>
        <peptide>
            <accession>222</accession>
            <sequence>CCC</sequence>
            <score>5000</score>
        </peptide>
        <peptide>
            <accession>222</accession>
            <sequence>CCC</sequence>
            <score>9000</score>
        </peptide>
        <peptide>
            <accession>222</accession>
            <sequence>CCC</sequence>
            <score>2000</score>
        </peptide>
  </peptides>
</data>

не предполагая, что способ сортировки непротиворечив, так что этот XML может иметь узлы в любом положении.

Затем я хочу сгруппировать узлы сначала по присоединению, затем внутри этих узлов, чтобы сгруппировать их по последовательности, а затем внутри этих узлов, чтобы отсортировать их по количеству баллов.

Таким образом, выходной XML будет таким

<data>
    <peptides>
        <peptide>
            <accession>111</accession>
            <sequence>AAA</sequence>
            <score>6000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>AAA</sequence>
            <score>5000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>AAA</sequence>
            <score>4000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>BBB</sequence>
            <score>8000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>BBB</sequence>
            <score>5000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>BBB</sequence>
            <score>5000</score>
        </peptide>
        <peptide>
            <accession>111</accession>
            <sequence>BBB</sequence>
            <score>1000</score>
        </peptide>
        <peptide>
            <accession>222</accession>
            <sequence>CCC</sequence>
            <score>9000</score>
        </peptide>
        <peptide>
            <accession>222</accession>
            <sequence>CCC</sequence>
            <score>5000</score>
        </peptide>
        <peptide>
            <accession>222</accession>
            <sequence>CCC</sequence>
            <score>2000</score>
        </peptide>
  </peptides>
</data>

РЕШЕНИЕ для этого последнего кода из любезно предоставленного ответа:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:key name="byAcc"    match="/data/peptides/peptide" use="accession" />
    <xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>
    <xsl:template match="/">
        <root>
            <xsl:apply-templates select="/data/proteins/protein">
            </xsl:apply-templates>
        </root>
    </xsl:template>
    <xsl:template match="/data/proteins/protein">
        <xsl:apply-templates select="key('byAcc',accession)">
            <xsl:sort select="sequence" data-type="text"/>
            <xsl:sort select="score" data-type="number"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="/data/peptides/peptide">
        <xsl:copy-of select="."/>
    </xsl:template>
</xsl:stylesheet>

проверьте здесь


person GWorking    schedule 18.12.2011    source источник
comment
Не устранит ли проблему начальная сортировка XML-документа по возрастанию, а затем применение преобразования? Не могли бы вы объяснить, что вы хотите сделать преобразование? Нынешняя формулировка довольно запутана. Наверное, если бы вы могли привести совсем упрощенный, небольшой пример, было бы понятнее. Затем, когда будет опубликовано решение этого упрощенного примера, вы сможете применить его к своей конкретной проблеме.   -  person Dimitre Novatchev    schedule 18.12.2011
comment
Извините, это была попытка привести простой пример. Я отредактирую вопрос.   -  person GWorking    schedule 18.12.2011
comment
@_Gerard: Спасибо за редактирование. Кажется, что если вы предварительно отсортируете XML-документ (по возрастанию на score) - первое из двух решений в моем ответе.   -  person Dimitre Novatchev    schedule 18.12.2011


Ответы (1)


Не совсем понятно, какая именно обработка требуется, но здесь я предлагаю две разные комбинации группировки и сортировки (я также предполагаю, что требуется XSLT 1.0):

Пусть у нас есть следующий очень простой XML-документ:

<nums>
 <num>5</num>
 <num>1</num>
 <num>2</num>
 <num>2</num>
 <num>9</num>
 <num>1</num>
 <num>5</num>
 <num>2</num>
 <num>9</num>
 <num>8</num>
</nums>

Если мы хотим устранить дубликаты, мы можем начать с этого преобразования:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:key name="kNumByVal" match="num" use="."/>

 <xsl:template match="/*">
  <nums>
    <xsl:copy-of select=
    "num
      [generate-id(key('kNumByVal', .)[1])
      =
       generate-id()
      ]"/>
  </nums>
 </xsl:template>
</xsl:stylesheet>

результат:

<nums>
   <num>5</num>
   <num>1</num>
   <num>2</num>
   <num>9</num>
   <num>8</num>
</nums>

Теперь, если мы хотим, чтобы в результате num элементов были отсортированы по значению, есть два разных решения:

  1. Отсортируйте исходный XML-документ, а затем выполните группировку:

...

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:key name="kNumByVal" match="num" use="."/>

 <xsl:template match="/*">

  <xsl:variable name="vrtfSorted">
   <xsl:for-each select="num">
    <xsl:sort data-type="number"/>
    <xsl:copy-of select="."/>
   </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="vSorted" select=
       "ext:node-set($vrtfSorted)/*"/>

  <nums>
   <xsl:for-each select=
    "$vSorted
      [generate-id(key('kNumByVal', .)[1])
      =
       generate-id()
      ]
     ">
    <xsl:sort data-type="number"/>
    <xsl:copy-of select="."/>
   </xsl:for-each>
  </nums>
 </xsl:template>
</xsl:stylesheet>

получен правильный результат:

<nums>
   <num>1</num>
   <num>2</num>
   <num>5</num>
   <num>8</num>
   <num>9</num>
</nums>

.2. Выполните группировку, а затем отсортируйте результат:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:key name="kNumByVal" match="num" use="."/>

 <xsl:template match="/*">

    <xsl:variable name="vDistinct" select=
    "num
      [generate-id(key('kNumByVal', .)[1])
      =
       generate-id()
      ]"/>

  <nums>
   <xsl:for-each select="$vDistinct">
    <xsl:sort data-type="number"/>
    <xsl:copy-of select="."/>
   </xsl:for-each>
  </nums>
 </xsl:template>
</xsl:stylesheet>

снова получен правильный результат:

<nums>
   <num>1</num>
   <num>2</num>
   <num>5</num>
   <num>8</num>
   <num>9</num>
</nums>

Обновление: после разъяснений в комментарии ОП, вот новый пример:

<nums>
  <num seq="1">01</num>
  <num seq="2">01</num>
  <num seq="1">01</num>
  <num seq="3">01</num>
  <num seq="2">01</num>
  <num seq="4">01</num>
  <num seq="1">02</num>
  <num seq="2">3</num>
  <num seq="3">04</num>
  <num seq="3">01</num>
  <num seq="4">02</num>
  <num seq="1">01</num>
</nums>

Необходимо группировать по @seq и по строковому значению элемента, а также сортировать по @seq и по строковому значению:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kBySeqVal" match="num"
  use="concat(@seq, '+', .)"/>

 <xsl:key name="kByValSeq" match="num"
  use="concat(., '+', @seq.)"/>

 <xsl:template match="/">
  <xsl:apply-templates select=
   "/*/*
     [generate-id()
     =
      generate-id(key('kBySeqVal',
                      concat(@seq, '+', .)
                      )
                       [1]
                  )
     ]
   ">
    <xsl:sort select="@seq" data-type="number"/>
    <xsl:sort select="." data-type="number"/>
   </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="num">
  <xsl:copy-of select="."/>
 </xsl:template>
</xsl:stylesheet>

и результат группируется и сортируется точно так, как нужно:

<num seq="1">01</num>
<num seq="1">02</num>
<num seq="2">01</num>
<num seq="2">3</num>
<num seq="3">01</num>
<num seq="3">04</num>
<num seq="4">01</num>
<num seq="4">02</num>
person Dimitre Novatchev    schedule 18.12.2011
comment
ваше решение работает, когда есть только одна группа данных, в вашем примере ‹num›. В моем примере это похоже на ‹num›‹seq›AAA‹/seq›‹val›1‹/val›‹/num›. Затем мне нужно, чтобы данные были отсортированы по ‹seq› И по ‹val›, поэтому сначала создайте группу по ‹seq›, а затем отсортируйте эту группу по ‹val›, или наоборот, создайте группу по ‹val›, а затем сортировка группы по ‹seq›. - person GWorking; 19.12.2011
comment
@Gerard: я обновил свой ответ (смотри до конца), указав решение для этого запрошенного случая. - person Dimitre Novatchev; 19.12.2011
comment
Вот именно, я не знал, что две сортировки можно писать последовательно :) Дополняю вопрос решением, примененным к XML-коду, и посмотрим, смогу ли я применить его к своему реальному коду. Но я бы сказал, что смогу :) Большое спасибо :D - person GWorking; 19.12.2011
comment
@Gerard: Добро пожаловать. Теперь вы также знаете, что я имею в виду под простыми примерами, и, надеюсь, будете давать нам их в своих будущих вопросах :) - person Dimitre Novatchev; 19.12.2011