Difference between revisions of "Key"

From APL Wiki
Jump to navigation Jump to search
(Created page with "{{Built-in|Key|⌸}} is a primitive monadic operator with an ambivalent operand where specified keys group the indices or major cells of an argument. It was introduced...")
 
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{Built-in|Key|⌸}} is a primitive [[monadic operator]] with an ambivalent [[operand]] where specified keys group the indices or major cells of an argument. It was introduced in [[Dyalog APL]] version 14.0 and is commonly compared to SQL's "GROUP BY" clause.
+
{{Built-in|Key|⌸}} is a [[primitive operator|primitive]] [[monadic operator]] which takes a [[dyadic function]] [[operand]] where specified keys group the indices or major cells of an argument. It was introduced in [[Dyalog APL]] version 14.0 and is commonly compared to SQL's [[wikipedia:Group by (SQL)|GROUP BY]] statement.
  
 
== Description ==
 
== Description ==
Monadically, key will group identical major cells together and applies the [[function]] operand f to each unique key, and the indices of the elements matching that key.
+
[[Monadic]]ally, Key will group identical [[major cell]]s together and applies the [[function]] operand once for each unique major cell. The function is applied with the unique major cell as left argument, while the right argument is the indices of major cells that match it:
  
 
<source lang=apl>
 
<source lang=apl>
Line 17: Line 17:
 
</source>
 
</source>
  
In the dyadic case, key applies f to the elements of the right argument corresponding to the unique elements of the left.
+
In the [[dyadic]] case, Key applies the function to collections of major cells from the right argument corresponding to unique elements of the left argument:
  
 
<source lang=apl>
 
<source lang=apl>
Line 32: Line 32:
 
</source>
 
</source>
  
In fact, the monadic case <source lang=apl inline>f⌸⍵</source> is equivalent to <source lang=apl inline>f⌸ ⍳≢⍵</source>
+
The monadic case, <source lang=apl inline>f⌸Y</source> is equivalent to <source lang=apl inline>Y f⌸ ⍳≢Y</source>.
 +
 
 +
== Problems ==
 +
=== Vocabulary ===
 +
A common problem with Key is the inability to control the order of the result (as Key will use the order of appearance) and the "vocabulary" (as Key will never include information for a major cell that doesn't occur). For example, here we want to count occurrences of the letters A, C, G, T:
 +
<source lang=apl>
 +
      {⍺,≢⍵}⌸'TCCGCGGTGGCG'
 +
T 2
 +
C 4
 +
G 6
 +
</source>
 +
Since A is entirely missing in the argument, it isn't mentioned in the result either. Likewise, the result is mis-ordered due to G and T appearing before the first C. A common solution is to inject the vocabulary before the actual data, and then decrement from the counts:
 +
<source lang=apl>      {⍺,¯1+≢⍵}⌸'ACGT','TCCGCGGTGGCG'
 +
A 0
 +
C 4
 +
G 6
 +
T 2
 +
</source>
 +
Now that the meaning of each count is known, the operand's left argument can be ignored, and the decrementing can be factored out from the operand:
 +
<source lang=apl>
 +
      ¯1+{≢⍵}⌸'ACGT','TCCGCGGTGGCG'
 +
0 4 6 2
 +
</source>
 +
=== Computing the unique ===
 +
Key computes the set of [[unique]] major cells. Often, this collection is needed separately from the occurrence information, but can be hard to extract. For example, to get the most frequently occurring letter:
 +
<source lang=apl>
 +
      ⊃⍒{≢⍵}⌸'TCCGCGGTGGCG'
 +
3
 +
</source>
 +
Notice that 3 is the index in the unique set of letters, and so it is tempting to write:
 +
<source lang=apl>
 +
      {(⊃⍒{≢⍵}⌸⍵)⌷∪⍵}'TCCGCGGTGGCG'
 +
G
 +
</source>
 +
However, while this code works, it is inefficient in that the unique is computed twice. This can be avoided by letting Key return the unique and using that:
 +
<source lang=apl>
 +
      (keys counts)←,⌿{⍺,≢⍵}⌸'TCCGCGGTGGCG'
 +
      keys⌷⍨⊃⍒counts
 +
G
 +
</source>
 +
Unfortunately, this can introduce a different inefficiency, in that the result of Key's operand can end up being a [[heterogeneous array]] (containing multiple [[datatype]]s), and these are stored as pointer arrays, consuming memory for one pointer per element, and forcing "pointer chasing" when addressing the data. A possible work-around is to collect the unique keys separately from the result of counts:
 +
<source lang=apl>
 +
      data←'TCCGCGGTGGCG'
 +
      keys←0⌿data
 +
      counts←{keys⍪←⍺ ⋄ ≢⍵}⌸data
 +
      keys⌷⍨⊃⍒counts
 +
G
 +
</source>
 +
If there are a large number of unique values, the repeated updating of the accumulating <source lang=apl inline>keys</source> variable can be an issue in itself.
  
 
== External links ==
 
== External links ==

Latest revision as of 09:28, 16 January 2022

Key () is a primitive monadic operator which takes a dyadic function operand where specified keys group the indices or major cells of an argument. It was introduced in Dyalog APL version 14.0 and is commonly compared to SQL's GROUP BY statement.

Description

Monadically, Key will group identical major cells together and applies the function operand once for each unique major cell. The function is applied with the unique major cell as left argument, while the right argument is the indices of major cells that match it:

      {⍺⍵}'Mississippi'
┌─┬────────┐
M1       
├─┼────────┤
i2 5 8 11
├─┼────────┤
s3 4 6 7 
├─┼────────┤
p9 10    
└─┴────────┘

In the dyadic case, Key applies the function to collections of major cells from the right argument corresponding to unique elements of the left argument:

      'Mississippi'{⍺⍵}'ABCDEFGHIJK' 
┌─┬────┐
MA   
├─┼────┤
iBEHK
├─┼────┤
sCDFG
├─┼────┤
pIJ  
└─┴────┘

The monadic case, fY is equivalent to Y f ⍳≢Y.

Problems

Vocabulary

A common problem with Key is the inability to control the order of the result (as Key will use the order of appearance) and the "vocabulary" (as Key will never include information for a major cell that doesn't occur). For example, here we want to count occurrences of the letters A, C, G, T:

      {,≢}'TCCGCGGTGGCG'
T 2
C 4
G 6

Since A is entirely missing in the argument, it isn't mentioned in the result either. Likewise, the result is mis-ordered due to G and T appearing before the first C. A common solution is to inject the vocabulary before the actual data, and then decrement from the counts:

      {,¯1+≢}'ACGT','TCCGCGGTGGCG'
A 0
C 4
G 6
T 2

Now that the meaning of each count is known, the operand's left argument can be ignored, and the decrementing can be factored out from the operand:

      ¯1+{}'ACGT','TCCGCGGTGGCG'
0 4 6 2

Computing the unique

Key computes the set of unique major cells. Often, this collection is needed separately from the occurrence information, but can be hard to extract. For example, to get the most frequently occurring letter:

      ⊃⍒{}'TCCGCGGTGGCG'
3

Notice that 3 is the index in the unique set of letters, and so it is tempting to write:

      {(⊃⍒{})⌷∪}'TCCGCGGTGGCG'
G

However, while this code works, it is inefficient in that the unique is computed twice. This can be avoided by letting Key return the unique and using that:

      (keys counts),{,≢}'TCCGCGGTGGCG'
      keys⊃⍒counts
G

Unfortunately, this can introduce a different inefficiency, in that the result of Key's operand can end up being a heterogeneous array (containing multiple datatypes), and these are stored as pointer arrays, consuming memory for one pointer per element, and forcing "pointer chasing" when addressing the data. A possible work-around is to collect the unique keys separately from the result of counts:

      data'TCCGCGGTGGCG'
      keys0data
      counts{keys  }data
      keys⊃⍒counts
G

If there are a large number of unique values, the repeated updating of the accumulating keys variable can be an issue in itself.

External links

Lessons

Documentation


APL built-ins [edit]
Primitive functions
Scalar
Monadic ConjugateNegateSignumReciprocalMagnitudeExponentialNatural LogarithmFloorCeilingFactorialNotPi TimesRollTypeImaginarySquare Root
Dyadic AddSubtractTimesDivideResiduePowerLogarithmMinimumMaximumBinomialComparison functionsBoolean functions (And, Or, Nand, Nor) ∙ GCDLCMCircularComplexRoot
Non-Scalar
Structural ShapeReshapeTallyDepthRavelEnlistTableCatenateReverseRotateTransposeRazeMixSplitEncloseNestCut (K)PairLinkPartitioned EnclosePartition
Selection FirstPickTakeDropUniqueIdentitySelectReplicateExpandSet functions (IntersectionUnionWithout) ∙ Bracket indexingIndex
Selector Index generatorGradeIndex OfInterval IndexIndicesDeal
Computational MatchNot MatchMembershipFindNub SieveEncodeDecodeMatrix InverseMatrix DivideFormatExecuteMaterialiseRange
Primitive operators Monadic EachCommuteConstantReplicateExpandReduceWindowed ReduceScanOuter ProductKeyI-beamSpawnFunction axis
Dyadic BindCompositions (Compose, Reverse Compose, Beside, Withe, Atop, Over) ∙ Inner ProductPowerAtUnderRankDepthVariantStencilCut (J)
Quad names
Arrays Index originMigration levelAtomic vector
Functions Name classCase convertUnicode convert
Operators SearchReplace