流式细胞仪FCS文件数据段,线性数据似乎有偏差

时间:2014-02-06 20:35:50

标签: c bioinformatics

最后和最后一次更新(我保证)

问题的核心是遍历数据,正如Jonathan Leffler提到的那样。二进制数据被“排列”在矩阵中。例如,如果我有3个事件和4个bitwidth 8参数,那么二进制数据

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

看起来像

00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00

我有两个for循环i& j,我需要用它来计算偏移量。

我最初有

(i * PAR * 2) + (j * PnB/8)

其中PAR是参数的数量,PnB是位宽,i是从0到总事件,j是从0到PAR。这是不正确的,不知道我是如何得到这个公式的。

==

我正在研究内部流量分析软件,并遇到了一些问题。我用于测试软件的FCS样本数据文件是使用MacOS 9 CellQuest上的FACSCaliber生成的。当我提取FSC-H和SSC-H的数据点时,我得不到与其他流程软件(即FlowJo)相同的结果。据我所知,在MacOS 9 CellQuest上生成的数据以大端顺序存储,并且相信我正在正确地转换数据:

for (int i = 0; i < params[j-1].PnB/8; ++i)
{
    lebyte[i] = (bytes[(params[j-1].PnB/8)-1-i] & 0xff) << i*8u;
    cx |= lebyte[i];
}

代码可能不太优雅,但它似乎做了我打算用已知数据样本做的事情。

PnB是位宽 PnR是通道值范围

使用实际流数据时得到的结果看起来是正确的,因为这些值在PnR指定的范围内,即如果PnR = 1024,则存储在16位空间中的数据在0 - 1023之间。

然而,当我绘制数据时,我得到一个倾斜的点图,其中散射朝FSC-H x轴弯曲。

以下摘录自FCS 3.1标准(流式细胞仪数据文件标准,国际细胞计数促进会;第13页):

  

$ BYTEORD / n1,n2,n3,n4 / $ BYTEORD / 4,3,2,1 / [REQUIRED]

     

此关键字指定数据的字节顺序,即用于在数据集中二进制存储数字数据值的字节顺序。该关键字的值对应于从数值上最低有效{1}到数值最高有效{4}的顺序,其中写入四个二进制数据字节以组成数据采集计算机中的32位字。数字用逗号分隔(ASCII 44)。只允许使用两个不同的值:

     
      
  • $ BYTEORD / 1,2,3,4 /(小端,即首先写入的最低有效字节,例如基于x86的个人计算机)

  •   
  • $ BYTEORD / 4,3,2,1 /(大端,即最后写入最低有效字节,例如,在切换到基于Intel的架构之前,包括较旧的Apple Macintosh计算机的PowerPC)   即使数据值的大小超过32位($ DATATYPE / D /)

  • ,也应使用其中一个值来指定字节序。   

如果我没有做好解释,我会提前道歉,并乐意在必要时进一步澄清任何要点。任何帮助将非常感谢。

更新 附图说明点。 图1

Skewed SCC-H x FSC-H

更新2

我制作了endian转换器的简化版本并进行了测试。

#include <stdio.h>
#include <stdlib.h>

int main() {
    int PnB = 16; // bitwidth of data stored for a specific channel value
    // for example the data value for sample A is stored in 16 bits.
    char bytes[PnB/8];
    unsigned int lebyte[PnB/8];
    unsigned int cx = 0;

    unsigned int b0, b1;

    /*  |  [0] |  [1] |
    *  | 0xff | 0x03 |
    */
    bytes[1] = 0x03;
    bytes[0] = 0xff;

    // in big endian print out

    b0 = (bytes[0] & 0xff) << 8u;
    b1 = bytes[1] & 0xff;

    cx = b0 | b1;

    printf("%d\n", cx);

    cx = 0;

    // convert to little endian

    for (int i = 0; i < PnB/8; ++i)
    {
        lebyte[i] = (bytes[i] & 0xff) << i*8u;
        cx |= lebyte[i];
    }

    printf("%d\n", cx);
}

结果是正确的:

  

65283

     

1023

所以我对原始源代码进行了更正(在原始帖子中进行了更改,以节省空间)。

另外,我正在做memcpy来从缓冲区复制数据。

memset(bytes, '\0', sizeof(char)*params[j-1].PnB/8);
memcpy(bytes, databuf+((i*data->PAR*2)+(j*params[j-1].PnB/8)), params[j-1].PnB/8);

但我仍然得到一个扭曲的数据。这可能与数据转换方式有关。我在网上找不到任何信息,我确信FlowJo的制造商不愿意分享这个秘密;)。我会继续看,看看我发现了什么。

更新3

抱歉让它更长但有其他信息:

typedef struct _fcs_parameter {
    double f1;          // logarithmic decade
    double f2;          // minimum value on log scale
    unsigned int PnB;   // bitwidth
    unsigned int PnR;   // range
    fcs_events *events; // event data
    char *sname;        // short name
    char *lname;        // filter name
} fcs_parameter;

2 个答案:

答案 0 :(得分:1)

仔细查看您显示的memset()memcpy()行。既然你没有展示i是如何设置的,也没有展示params结构数组中的内容,那么它有点难以解释。但是,如果PnB成员中有任何不同的尺寸,那么我认为您的memcpy()偏移计算是假的。

这是一些代码及其输出;你将不得不适应你的情况。最后一部分尝试模拟您的memset/memcpy代码,因为没有您所显示的许多变量的解释。它包括一种似乎更有意义的替代解释。

假设你有C99,你可以将一些功能变成static inline。还有其他一些代码也假设C99。将它修复到C89并不难,但我不打算为你做这件事。

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static uint16_t convert_uint16be(uint8_t const *bytes)
{
    uint16_t r = (bytes[0] << 8) | bytes[1];
    return r;
}

static uint32_t convert_uint32be(uint8_t const *bytes)
{
    uint32_t r = (((((bytes[0] << 8) | bytes[1]) << 8) | bytes[2]) << 8) | bytes[3];
    return r;
}

static void print16(uint8_t const *bytes)
{
  uint16_t r1 = convert_uint16be(bytes);
  int16_t  r2 = convert_uint16be(bytes);
  printf("0x%.2X 0x%.2X = 0x%.4" PRIX16 " = %6" PRId16 "\n", bytes[0], bytes[1], r1, r2);
}

static void print32(uint8_t const *bytes)
{
  uint32_t r1 = convert_uint32be(bytes);
  int32_t  r2 = convert_uint32be(bytes);
  printf("0x%.2X 0x%.2X 0x%.2X 0x%.2X = 0x%.8" PRIX32 " = %11" PRId32 "\n", bytes[0], bytes[1], bytes[2], bytes[3], r1, r2);
}

int main(void)
{
    int PnB = 16; // bitwidth of data stored for a specific channel value
    // for example the data value for sample A is stored in 16 bits.
    char bytes[PnB/8];
    unsigned int lebyte[PnB/8];
    unsigned int cx = 0;
    unsigned int b0, b1;

    /*  |  [0] |  [1] |
     *  | 0xff | 0x03 |
     */
    bytes[0] = 0xff;
    bytes[1] = 0x03;

    // in big endian print out
    b0 = (bytes[0] & 0xff) << 8u;
    b1 = bytes[1] & 0xff;
    cx = b0 | b1;

    printf("%5d = 0x%.4X\n", cx, cx);

    // convert to little endian
    cx = 0;
    for (int i = 0; i < PnB/8; ++i)
    {
        lebyte[i] = (bytes[i] & 0xff) << i*8u;
        cx |= lebyte[i];
    }
    printf("%5d = 0x%.4X\n", cx, cx);

    print16((uint8_t *)bytes);

    uint8_t data[] =
    {
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x03, 0xFF,
      0x00, 0x00, 0xFF, 0xFF,
      0x08, 0x08, 0x09, 0xC0,
      0x80, 0x80, 0x90, 0x0C,
      0xFF, 0xFF, 0xED, 0xBC,
    };
    int data_size = sizeof(data) / sizeof(data[0]);

    for (int i = 0; i < data_size; i += 2)
      print16(&data[i]);
    for (int i = 0; i < data_size; i += 4)
      print32(&data[i]);

    {
      struct { int PnB; } params[] = { { 16 }, { 16 }, { 32 }, { 16 }, { 16 }, };
      int num_params = sizeof(params) / sizeof(params[0]);
      uint8_t value[4];
      int i = 0;
      int num = num_params;
      int offset = 0;
      for (int j = 1; j <= num; j++)
      {
        memset(value, '\0', sizeof(char)*params[j-1].PnB/8);
        printf("i = %2d; j = %2d; offset = %2d; calc = %2d; size = %2d\n",
               i, j, offset, ((i*7*2)+(j*params[j-1].PnB/8)), params[j-1].PnB/8);
        /* The calculation works plausibly when all params[n].PnB are the same
         * size, but not otherwise
         */
        memcpy(value, data+((i*7*2)+(j*params[j-1].PnB/8)), params[j-1].PnB/8);
        if (params[j].PnB == 16)
          print16(value);
        else
          print32(value);
        memcpy(value, data+offset, params[j-1].PnB/8);
        if (params[j].PnB == 16)
          print16(value);
        else
          print32(value);
        offset += params[j-1].PnB/8;
      }
    }

    return 0;
}

示例输出:

65283 = 0xFF03
 1023 = 0x03FF
0xFF 0x03 = 0xFF03 =   -253
0x00 0x00 = 0x0000 =      0
0x00 0x00 = 0x0000 =      0
0x00 0x00 = 0x0000 =      0
0x03 0xFF = 0x03FF =   1023
0x00 0x00 = 0x0000 =      0
0xFF 0xFF = 0xFFFF =     -1
0x08 0x08 = 0x0808 =   2056
0x09 0xC0 = 0x09C0 =   2496
0x80 0x80 = 0x8080 = -32640
0x90 0x0C = 0x900C = -28660
0xFF 0xFF = 0xFFFF =     -1
0xED 0xBC = 0xEDBC =  -4676
0x00 0x00 0x00 0x00 = 0x00000000 =           0
0x00 0x00 0x03 0xFF = 0x000003FF =        1023
0x00 0x00 0xFF 0xFF = 0x0000FFFF =       65535
0x08 0x08 0x09 0xC0 = 0x080809C0 =   134744512
0x80 0x80 0x90 0x0C = 0x8080900C = -2139058164
0xFF 0xFF 0xED 0xBC = 0xFFFFEDBC =       -4676
i =  0; j =  1; offset =  0; calc =  2; size =  2
0x00 0x00 = 0x0000 =      0
0x00 0x00 = 0x0000 =      0
i =  0; j =  2; offset =  2; calc =  4; size =  2
0x00 0x00 0x00 0x00 = 0x00000000 =           0
0x00 0x00 0x00 0x00 = 0x00000000 =           0
i =  0; j =  3; offset =  4; calc = 12; size =  4
0x08 0x08 = 0x0808 =   2056
0x00 0x00 = 0x0000 =      0
i =  0; j =  4; offset =  8; calc =  8; size =  2
0x00 0x00 = 0x0000 =      0
0x00 0x00 = 0x0000 =      0
i =  0; j =  5; offset = 10; calc = 10; size =  2
0xFF 0xFF 0x03 0xFF = 0xFFFF03FF =      -64513
0xFF 0xFF 0x03 0xFF = 0xFFFF03FF =      -64513

答案 1 :(得分:0)

问题是我用来计算偏移量的公式。

我应该使用以下内容:

for (int i = 0; i < data->TOT; ++i)
    {
        for (int j = 0; j < data->PAR; ++j)
        {

        // code removed for brevity

        memset(bytes, '\0', sizeof(char)*params[j].PnB/8);
        memcpy(bytes, databuf+((i*data->PAR*params[j].PnB/8)+(j*params[j].PnB/8)), params[j].PnB/8);

        // more code here
        }
}

感谢您的帮助!如果您没有提到PnB问题,我不会意识到问题是我计算偏移的方式。