1

I am new to PDL. R's ifelse() method can do conditonal element selection. For example,

x <- c(1,2,3,4)
ifelse(x%%2, x, x*2)
# [1] 1 4 3 8

Anyone knows how to do this in PDL? I know you can do it like below, but is there any better ways?

pdl(map { $_ % 2 ? $_ : $_*2 } @{$x->unpdl} )

4 Answers4

2
#! /usr/bin/perl
use warnings;
use strict;

use PDL;

my $x     = 'PDL'->new([1, 2, 3, 4]);
my $where = ! ($x % 2);               # [0 1 0 1]
my $y     = $x * ($where + 1);
print $y;                             # [1 4 3 8]

or, shortly

my $y = $x * ( 2 - $x % 2 );
choroba
  • 231,213
  • 25
  • 204
  • 289
  • 1
    Sorry but I'm not asking for how to solve that particular example. I'm actually asking for a generic way of abstraction like R's ifelse() method. – Stephan Loyd Feb 05 '18 at 13:58
  • It's not only not really idiomatic PDL, it also doesn't work as efficiently as other solutions here, because the multiplication step has to scan the whole of `$x`. – Ed. Mar 18 '22 at 16:37
1

Answering the question myself. It can be something like this,

use PDL;                                                                      

sub ifelse {                                                                  
    my ( $test, $yes, $no ) = @_;                                             

    $test = pdl($test);                                                       
    my ( $ok, $nok ) = which_both($test);                                     

    my $rslt = zeros( $test->dim(0) );                                        

    unless ( $ok->isempty ) {                                                 
        $yes = pdl($yes);                                                     
        $rslt->slice($ok) .= $yes->index( $ok % $yes->dim(0) );               
    }                                                                         
    unless ( $nok->isempty ) {                                                
        $no = pdl($no);                                                       
        $rslt->slice($nok) .= $no->index( $nok % $no->dim(0) );               
    }                                                                         
    return $rslt;                                                             
}                                                                             

my $x = pdl( 1, 2, 3, 4 );                                                    
say ifelse( $x % 2, $x, $x * 2 );       # [1 4 3 8]                                             
say ifelse( $x % 2, 5, sequence( 3 ) ); # [5 1 5 0]                                      
say ifelse( 42, $x, $x * 2 );           # [1]
1

In PDL, the general solution to that sort of thing likely involves slicing and similar. Looking at the latest release notes of PDL (2.077), which has a new where_both, I remembered this question (disclosure: I'm current maintainer). While your specific problem only involves a change to the values of even numbers, I'll also show the case of adding 2 to odds:

my ($odd, $even) = where_both($x, $x % 2);
$odd += 2, $even *= 2; # the "," form is just a flourish, it could be 2 lines

It's efficient, in proper PDL style, because the scan of $x only happens once (you won't be surprised to learn it also uses which_both under the hood), and the mutations only look at the slices of the bits that are relevant. This is very similar to your code, but it got captured into a small, widely-reusable function. (I wrote it to turn the TriD EuclidAxes stuff from using Perl for-loops to actually using ndarrays, if you're interested)

Ed.
  • 1,992
  • 1
  • 13
  • 30
0

Better than $x ? $y : $z? Not to my mind, but it's a matter of style and taste

sub ifelse {
    my ($x,$y,$z) = @_;

    $x ? $y : $z ;
    if($x){$y}else{$z} ;
    [$y,$z]->[!$x] ;
    [$z,$y]->[!!$x] ;
    ($x && $y) || $z ;        # valid only if $y is always true
    (!$x && $z) || $y ;       # valid only if $z is always true
}
mob
  • 117,087
  • 18
  • 149
  • 283
  • 1
    No I'm not asking for "normal" Perl. I'm asking for [PDL](https://metacpan.org/pod/distribution/PDL/Basic/Pod/QuickStart.pod#To-create-a-new-PDL-variable). Things like $x?$y:$z do not work for PDL. – Stephan Loyd Feb 05 '18 at 13:55