子集面板数据以连续的长度字符串为条件

时间:2018-01-25 17:35:56

标签: r dplyr subset panel-data

我一直试图使用dplyr对某些面板数据(即组内的ID)进行子集化。

我希望确定每个组中的idgrp,其中NUM系列的最小值小于2且最大值大于2.我已构建下面的 minimal 工作示例应该说明问题。

我一直在使用filter()row_number() == c(1,n()),并尝试将其分开并合并,即不同类型的_join,它重新组合在一起,但我卡住了,我就是现在转向SO社区寻求帮助。

我得到了什么

这样的tibble

df <- tibble(id = rep(0:1, c(8, 13)), grp = rep(c("01", "02"), c(13, 8)),
             NUM = c(-4, -3, -2, -1, 1, 2, 3, 4, -3, -2, -1,
                      1, 2, -3, -2, -1, 1, 2, 3, 4, 5)) %>% group_by(id, grp)
df %>% print(n=21)
#> # A tibble: 21 x 3
#> # Groups:   id, grp [3]
#>       id   grp   NUM
#>    <int> <chr> <dbl>
#>  1     0    01    -4
#>  2     0    01    -3
#>  3     0    01    -2
#>  4     0    01    -1
#>  5     0    01     1
#>  6     0    01     2
#>  7     0    01     3
#>  8     0    01     4
#>  9     1    01    -3
#> 10     1    01    -2
#> 11     1    01    -1
#> 12     1    01     1
#> 13     1    01     2
#> 14     1    02    -3
#> 15     1    02    -2
#> 16     1    02    -1
#> 17     1    02     1
#> 18     1    02     2
#> 19     1    02     3
#> 20     1    02     4
#> 21     1    02     5

我想获得什么/期望的结果

df_out <- tibble(id = rep(0:1, c(9, 8)),
             grp = rep(c("01", "02"), c(9, 8)),
             NUM = c(-4, -3, -2, -1, 1, 2, 3,
           4, 5, -3, -2, -1, 1, 2, 3, 4, 5)) %>%  group_by(id, grp)
df_out
#> # A tibble: 17 x 3
#> # Groups:   id, grp [3]
#>       id   grp   NUM
#>    <int> <chr> <dbl>
#>  1     0    01    -4
#>  2     0    01    -3
#>  3     0    01    -2
#>  4     0    01    -1
#>  5     0    01     1
#>  6     0    01     2
#>  7     0    01     3
#>  8     0    01     4
#>  9     1    02    -3
#> 10     1    02    -2
#> 11     1    02    -1
#> 12     1    02     1
#> 13     1    02     2
#> 14     1    02     3
#> 15     1    02     4
#> 16     1    02     5

1 个答案:

答案 0 :(得分:2)

喜欢这样吗?

NUM

此外,如果要对精确值进行分组,例如第一个-3NUM且最后5df %>% group_by(id, grp) %>% mutate(first = first(NUM) ,last = last(NUM)) %>% filter(-3 == first & 5 == last) %>% select(-first, -last) #> # A tibble: 8 x 3 #> # Groups: id, grp [1] #> id grp NUM #> <int> <chr> <dbl> #> 1 1 02 -3 #> 2 1 02 -2 #> 3 1 02 -1 #> 4 1 02 1 #> 5 1 02 2 #> 6 1 02 3 #> 7 1 02 4 #> 8 1 02 5 ,即第9-16行原始数据,这可以这样做,

export class CanvasComponent implements OnInit {

  sphereRad = 280;
  radius_sp = 1;
  distance = 600;
  particle_size = 0.7;
  constructor() { }

  ngOnInit() {
    this.canvasApp();
  }

  canvasApp () {
    const canvas = document.querySelector('canvas');
    const context = canvas.getContext('2d');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    let displayWidth;
    let displayHeight;
    let wait;
    let count;
    let numToAddEachFrame;
    let particleList;
    let recycleBin;
    let particleAlpha;
    let r, g, b;
    let fLen;
    let m;
    let projCenterX;
    let projCenterY;
    let zMax;
    let turnAngle;
    let turnSpeed;
    let sphereCenterX, sphereCenterY, sphereCenterZ;
    let particleRad;
    let zeroAlphaDepth;
    let randAccelX, randAccelY, randAccelZ;
    let gravity;
    let rgbString;
    // we are defining a lot of letiables used in the screen update functions globally so that they don't have to be redefined every frame.
    let p;
    let outsideTest;
    let nextParticle;
    let sinAngle;
    let cosAngle;
    let rotX, rotZ;
    let depthAlphaFactor;
    let i;
    let theta, phi;
    let x0, y0, z0;

    // INITIALLI
    const init = () => {
      wait = 1;
      count = wait - 1;
      numToAddEachFrame = 30;

      // particle color
      r = 255;
      g = 255;
      b = 255;

      rgbString = 'rgba(' + r + ',' + g + ',' + b + ','; // partial string for color which will be completed by appending alpha value.
      particleAlpha = 1; // maximum alpha

      displayWidth = canvas.width;
      displayHeight = canvas.height;

      fLen = this.distance; // represents the distance from the viewer to z=0 depth.

      // projection center coordinates sets location of origin
      projCenterX = displayWidth / 2;
      projCenterY = displayHeight / 2;

      // we will not draw coordinates if they have too large of a z-coordinate (which means they are very close to the observer).
      zMax = fLen - 2;

      particleList = {};
      recycleBin = {};

      // random acceleration factors - causes some random motion
      randAccelX = 0.1;
      randAccelY = 0.1;
      randAccelZ = 0.1;

      gravity = -0; // try changing to a positive number (not too large, for example 0.3), or negative for floating upwards.

      particleRad = this.particle_size;

      sphereCenterX = 0;
      sphereCenterY = 0;
      sphereCenterZ = -3 - this.sphereRad;

      // alpha values will lessen as particles move further back, causing depth-based darkening:
      zeroAlphaDepth = 0;

      turnSpeed = 2 * Math.PI / 1200; // the sphere will rotate at this speed (one complete rotation every 1600 frames).
      turnAngle = 0; // initial angle

      // timer = setInterval(onTimer, 10 / 24);
      onTimer();
    }

    const onTimer = () => {
      // if enough time has elapsed, we will add new particles.
      count++;
      if (count >= wait) {

        count = 0;
        for (i = 0; i < numToAddEachFrame; i++) {
          theta = Math.random() * 2 * Math.PI;
          phi = Math.acos(Math.random() * 2 - 1);
          x0 = this.sphereRad * Math.sin(phi) * Math.cos(theta);
          y0 = this.sphereRad * Math.sin(phi) * Math.sin(theta);
          z0 = this.sphereRad * Math.cos(phi);

          // We use the addParticle function to add a new particle. The parameters set the position and velocity components.
          // Note that the velocity parameters will cause the particle to initially fly outwards away from the sphere center (after
          // it becomes unstuck).
          const p = addParticle(x0, sphereCenterY + y0, sphereCenterZ + z0, 0.002 * x0, 0.002 * y0, 0.002 * z0);

          // we set some 'envelope' parameters which will control the evolving alpha of the particles.
          p.attack = 50;
          p.hold = 50;
          p.decay = 100;
          p.initValue = 0;
          p.holdValue = particleAlpha;
          p.lastValue = 0;

          // the particle will be stuck in one place until this time has elapsed:
          p.stuckTime = 90 + Math.random() * 20;

          p.accelX = 0;
          p.accelY = gravity;
          p.accelZ = 0;
        }
      }

      // update viewing angle
      turnAngle = (turnAngle + turnSpeed) % (2 * Math.PI);
      sinAngle = Math.sin(turnAngle);
      cosAngle = Math.cos(turnAngle);

      // background fill
      context.fillStyle = '#000000';
      context.fillRect(0, 0, displayWidth, displayHeight);

      // update and draw particles
      p = particleList.first;
      while (p != null) {
        // before list is altered record next particle
        nextParticle = p.next;

        // update age
        p.age++;

        // if the particle is past its 'stuck' time, it will begin to move.
        if (p.age > p.stuckTime) {
          p.velX += p.accelX + randAccelX * (Math.random() * 2 - 1);
          p.velY += p.accelY + randAccelY * (Math.random() * 2 - 1);
          p.velZ += p.accelZ + randAccelZ * (Math.random() * 2 - 1);

          p.x += p.velX;
          p.y += p.velY;
          p.z += p.velZ;
        }

        /*
              We are doing two things here to calculate display coordinates.
              The whole display is being rotated around a vertical axis, so we first calculate rotated coordinates for
              x and z (but the y coordinate will not change).
              Then, we take the new coordinates (rotX, y, rotZ), and project these onto the 2D view plane.
              */
        rotX =  cosAngle * p.x + sinAngle * (p.z - sphereCenterZ);
        rotZ =  -sinAngle * p.x + cosAngle * (p.z - sphereCenterZ) + sphereCenterZ;
        // m = this.radius_sp * fLen / (fLen - rotZ);
        m = this.radius_sp;
        p.projX = rotX * m + projCenterX;
        p.projY = p.y * m + projCenterY;
        p.projZ = rotZ * m + projCenterX;
        // update alpha according to envelope parameters.
        if (p.age < p.attack + p.hold + p.decay) {
          if (p.age < p.attack) {
            p.alpha = (p.holdValue - p.initValue) / p.attack * p.age + p.initValue;
          } else if (p.age < p.attack + p.hold) {
            p.alpha = p.holdValue;
          } else if (p.age < p.attack + p.hold + p.decay) {
            p.alpha = (p.lastValue - p.holdValue) / p.decay * (p.age - p.attack - p.hold) + p.holdValue;
          }
        } else {
          p.dead = true;
        }

        // see if the particle is still within the viewable range.
        if ((p.projX > displayWidth) || (p.projX < 0) || (p.projY < 0) || (p.projY > displayHeight) || (rotZ > zMax)) {
          outsideTest = true;
        } else {
          outsideTest = false;
        }
        if (outsideTest || p.dead ||
          (p.projX  > displayWidth / (2 + (1 - Math.random())) && p.projZ + displayWidth * 0.1  > displayWidth / 2) ||
          (p.projX < displayWidth / (2 - (1 - Math.random()))  && p.projZ + displayWidth * 0.25  < displayWidth / 2)
        ) {
          recycle(p);
        } else {
          // depth-dependent darkening
          // console.log(turnAngle, rotZ)
          depthAlphaFactor = 1;
          // depthAlphaFactor = (1 - (1.5 + rotZ / 100));
          depthAlphaFactor = (depthAlphaFactor > 1) ? 1 : ((depthAlphaFactor < 0) ? 0 : depthAlphaFactor);
          context.fillStyle = rgbString + depthAlphaFactor * p.alpha + ')';
          // draw
          context.beginPath();
          context.arc(p.projX, p.projY, m * particleRad, 0, 2 * Math.PI, false);
          context.closePath();
          context.fill();
        }

        p = nextParticle;
      }
      window.requestAnimationFrame(onTimer);
    }

    const addParticle = (x0, y0, z0, vx0, vy0, vz0) => {
      let newParticle;
      // const color;


      // check recycle bin for available drop:
      if (recycleBin.first != null) {
        newParticle = recycleBin.first;
        // remove from bin
        if (newParticle.next != null) {
          recycleBin.first = newParticle.next;
          newParticle.next.prev = null;
        } else {
          recycleBin.first = null;
        }
      } else {
        newParticle = {};
      }
      // if the recycle bin is empty, create a new particle (a new empty object):
      // add to beginning of particle list
      if (particleList.first == null) {
        particleList.first = newParticle;
        newParticle.prev = null;
        newParticle.next = null;
      } else {
        newParticle.next = particleList.first;
        particleList.first.prev = newParticle;
        particleList.first = newParticle;
        newParticle.prev = null;
      }

      // initialize
      newParticle.x = x0;
      newParticle.y = y0;
      newParticle.z = z0;
      newParticle.velX = vx0;
      newParticle.velY = vy0;
      newParticle.velZ = vz0;
      newParticle.age = 0;
      newParticle.dead = false;

      if (Math.random() < 0.5) {
        newParticle.right = true;
      } else {
        newParticle.right = false;
      }
      return newParticle;
    }

    const recycle = (p) => {
      // remove from particleList
      if (particleList.first === p) {
        if (p.next != null) {
          p.next.prev = null;
          particleList.first = p.next;
        } else {
          particleList.first = null;
        }
      } else {
        if (p.next == null) {
          p.prev.next = null;
        } else {
          p.prev.next = p.next;
          p.next.prev = p.prev;
        }
      }
      // add to recycle bin
      if (recycleBin.first == null) {
        recycleBin.first = p;
        p.prev = null;
        p.next = null;
      } else {
        p.next = recycleBin.first;
        recycleBin.first.prev = p;
        recycleBin.first = p;
        p.prev = null;
      }
    };
    init();
  }
}

上述内容受this SO answer启发。