【问题标题】:Stacking Arrays in Numpy: Different behaviors between UNIX and Windows在 Numpy 中堆叠数组:UNIX 和 Windows 之间的不同行为
【发布时间】:2017-03-24 21:30:28
【问题描述】:

注意:这是 Python 2.7,而不是 Py3

这是对较早问题的更新尝试。您要求我提供完整的代码、内容解释和示例输出文件。我会尽力把它格式化好。

此代码旨在从荧光“读板器”获取输入文件,并将读数转换为 DNA 浓度和质量。然后它会生成一个根据 8x12 板方案(DNA/分子工作的标准)组织的输出文件。行标记为“A、B、C、...、H”,列标记为 1 - 12。

根据用户输入,需要堆叠数组以格式化输出。但是,当数组在 UNIX 中堆叠(并打印或写入输出文件)时,它们仅限于第一个字符

换句话说,在 Windows 中,如果数组中的数字是 247.5,它会打印完整的数字。但在 UNIX 环境(Linux/Ubuntu/MacOS)中,它会被截断为简单的“2”。 -2.7 的数字将在 Windows 中正常打印,但在 UNIX 中仅打印为“-”。

完整的代码可以在下面找到; 请注意,最后一块是代码中最相关的部分

#!/usr/bin/env python

Usage = """
plate_calc.py - version 1.0

Convert a series of plate fluorescence readings
to total DNA mass per sample and print them to 
a tab-delimited output file.

This program can take multiple files as inputs
(separated by a space) and generates a new
output file for each input file.

NOTE: 

1) Input(s) must be an exported .txt file.
2) Standards must be in columns 1 and 2, or 11 
and 12.
3) The program assumes equal volumes across wells.

Usage:

    plate_calc.py input.txt input2.txt input3.txt
"""

import sys
import numpy as np

if len(sys.argv)<2:
    print Usage
else:
#First, we want to extract the values of interest into a Numpy array
    Filelist = sys.argv[1:]
    input_DNA_vol = raw_input("Volume of sample used for AccuClear reading (uL): ")
    remainder_vol = raw_input("Remaining volume per sample (uL): ")
    orientation = raw_input("Are the standards on the LEFT (col. 1 & 2), or on the RIGHT (col. 11 and 12)? ")
    orientation = orientation.lower()
    for InfileName in Filelist:
        with open(InfileName) as Infile:
            fluor_list = []
            Linenumber = 1
            for line in Infile: #this will extract the relevant information and store as a list of lists
                if Linenumber == 5:
                    line = line.strip('\n').strip('\r').strip('\t').split('\t')
                    fluor_list.append(line[1:])
                elif Linenumber > 5 and Linenumber < 13:
                    line = line.strip('\n').strip('\r').strip('\t').split('\t')
                    fluor_list.append(line)
                Linenumber += 1
            fluor_list = [map(float, x) for x in fluor_list] #converts list items from strings to floats
            fluor_array = np.asarray(fluor_list) #this takes our list of lists and converts it to a numpy array

这部分代码(上图)从输入文件(从读板器获得)中提取感兴趣的值,并将它们转换为数组。它还需要用户输入来获取计算和转换的信息,并确定放置标准的列。

最后一部分将在稍后发挥作用,当数组堆叠时 - 这就是问题行为发生的地方。

        #Create conditional statement, depending on where the standards are, to split the array
        if orientation == "right":
            #Next, we want to average the 11th and 12th values of each of the 8 rows in our numpy array 
            stds = fluor_array[:,[10,11]] #Creates a sub-array with the standard values (last two columns, (8x2))
            data = np.delete(fluor_array,(10,11),axis=1) #Creates a sub-array with the data (first 10 columns, (8x10))

        elif orientation == "left":
            #Next, we want to average the 1st and 2nd values of each of the 8 rows in our numpy array   
            stds = fluor_array[:,[0,1]] #Creates a sub-array with the standard values (first two columns, (8x2))
            data = np.delete(fluor_array,(0,1),axis=1) #Creates a sub-array with the data (last 10 columns, (8x10))

        else:
            print "Error: answer must be 'LEFT' or 'RIGHT'"

        std_av = np.mean(stds, axis=1) #creates an array of our averaged std values

        #Then, we want to subtract the average value from row 1 (the BLANK) from each of the 8 averages (above)
        std_av_st = std_av - std_av[0]

        #Run a linear regression on the points in std_av_st against known concentration values (these data = y axis, need x axis)
        x = np.array([0.00, 0.03, 0.10, 0.30, 1.00, 3.00, 10.00, 25.00])*10 #ng/uL*10 = ng/well
        xi = np.vstack([x, np.zeros(len(x))]).T #creates new array of (x, 0) values (for the regression only); also ensures a zero-intercept (when we use (x, 1) values, the y-intercept is not forced to be zero, and the slope is slightly inflated)
        m, c = np.linalg.lstsq(xi, std_av_st)[0] # m = slope for future calculations

        #Now we want to subtract the average value from row 1 of std_av (the average BLANK value) from all data points in "data"
        data_minus_blank = data - std_av[0]

        #Now we want to divide each number in our "data" array by the value "m" derived above (to get total ng/well for each sample; y/m = x)
        ng_per_well = data_minus_blank/m

        #Now we need to account for the volume of sample put in to the AccuClear reading to calculate ng/uL
        ng_per_microliter = ng_per_well/float(input_DNA_vol)

        #Next, we multiply those values by the volume of DNA sample (variable "ng")
        ng_total = ng_per_microliter*float(remainder_vol)

        #Set number of decimal places to 1
        ng_per_microliter = np.around(ng_per_microliter, decimals=1)
        ng_total = np.around(ng_total, decimals=1)

上述代码执行必要的计算,以根据 DNA“标准”的线性回归计算给定样本中 DNA 的浓度 (ng/uL) 和总质量 (ng),该标准可以在列中1 和 2(用户输入 =“左”)或第 11 和 12 列(用户输入 =“右”)。

        #Create a row array (values A-H), and a filler array ('-') to add to existing arrays
        col = [i for i in range(1,13)]
        row = np.asarray(['A','B','C','D','E','F','G','H'])
        filler = np.array(['-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-',]).reshape((8,2))

上面的代码创建了要与原始数组堆叠的数组。 “填充”数组是根据用户输入的“右”或“左”放置的(堆叠命令 np.c_[ ] 如下所示)。

        #Create output
        Outfile = open('Total_DNA_{0}'.format(InfileName),"w")
        Outfile.write("DNA concentration (ng/uL):\n\n")
        Outfile.write("\t"+"\t".join([str(n) for n in col])+"\n")
        if orientation == "left": #Add filler to left, then add row to the left of filler
            ng_per_microliter = np.c_[filler,ng_per_microliter]
            ng_per_microliter = np.c_[row,ng_per_microliter]
            Outfile.write("\n".join(["\t".join([n for n in item]) for item in ng_per_microliter.tolist()])+"\n\n")
        elif orientation == "right": #Add rows to the left, and filler to the right
            ng_per_microliter = np.c_[row,ng_per_microliter]
            ng_per_microliter = np.c_[ng_per_microliter,filler]
            Outfile.write("\n".join(["\t".join([n for n in item]) for item in ng_per_microliter.tolist()])+"\n\n")
        Outfile.write("Total mass of DNA per sample (ng):\n\n")
        Outfile.write("\t"+"\t".join([str(n) for n in col])+"\n")
        if orientation == "left":
            ng_total = np.c_[filler,ng_total]
            ng_total = np.c_[row,ng_total]
            Outfile.write("\n".join(["\t".join([n for n in item]) for item in ng_total.tolist()]))
        elif orientation == "right":
            ng_total = np.c_[row,ng_total]
            ng_total = np.c_[ng_total,filler]
            Outfile.write("\n".join(["\t".join([n for n in item]) for item in ng_total.tolist()]))
        Outfile.close

最后,我们生成了输出文件。这就是问题行为发生的地方。

使用简单的打印命令,我发现堆叠命令 numpy.c_[ ] 是罪魁祸首(不是数组写入命令)。

看来 numpy.c_[ ] 在 Windows 中不会截断这些数字,但会在 UNIX 环境中将这些数字限制为第一个字符。 p>

有哪些替代方案可能适用于两个平台?如果不存在,我不介意制作一个特定于 UNIX 的脚本。

感谢大家的帮助和耐心。很抱歉没有提前提供所有必要的信息。

这些图片是屏幕截图,显示了 Windows 的正确输出以及我最终在 UNIX 中得到的结果(我试图为你格式化这些......但它们是一场噩梦)。当我简单地打印数组“ng_per_microliter”和“ng_total”时,我还包含了在终端中获得的输出截图。

【问题讨论】:

  • 你走错了方向。使问题和示例更紧凑,专注于手头的问题,而不是更大更难理解。这看起来像一个格式问题。 ng_per_microliterng_total 这样的值是否正确?我假设它们是具有形状和 dtype 的数组。
  • 在我的辩护中,这就是我分解代码的原因,之前的评论者曾要求我这样做。

标签: python arrays windows unix numpy


【解决方案1】:

使用简单的打印命令,我发现堆叠命令 numpy.c_[ ] 是罪魁祸首(不是数组写入命令)。

因此 numpy.c_[ ] 似乎不会在 Windows 中截断这些数字,但会将这些数字限制为 UNIX 环境中的第一个字符。

用简单的例子来说明这些陈述。 np.c_[] 不应该做任何不同的事情。

在 Py3 中,这里默认的字符串类型是 unicode。和 numpy 1.12

In [149]: col = [i for i in range(1,13)]
     ...: row = np.asarray(['A','B','C','D','E','F','G','H'])
     ...: filler = np.array(['-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-',]).reshape((8,2))
     ...: 
In [150]: col
Out[150]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
In [151]: "\t"+"\t".join([str(n) for n in col])+"\n"
Out[151]: '\t1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\n'
In [152]: filler
Out[152]: 
array([['-', '-'],
       ...
       ['-', '-'],
       ['-', '-']], 
      dtype='<U1')
In [153]: row
Out[153]: 
array(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'], 
      dtype='<U1')
In [154]: row.shape
Out[154]: (8,)
In [155]: filler.shape
Out[155]: (8, 2)
In [159]: ng_per_microliter=np.arange(8.)+1.23
In [160]: np.c_[filler,ng_per_microliter]
Out[160]: 
array([['-', '-', '1.23'],
       ['-', '-', '2.23'],
       ['-', '-', '3.23'],
      ...
       ['-', '-', '8.23']], 
      dtype='<U32')
In [161]: np.c_[row,ng_per_microliter]
Out[161]: 
array([['A', '1.23'],
       ['B', '2.23'],
       ['C', '3.23'],
         ....
       ['H', '8.23']], 
      dtype='<U32')

在早期的numpy 版本中,U1(或 Py2 中的 S1)数组与数值的连接可能会将 dtype 留在U1。在我的示例中,它们已扩展为 U32

因此,如果您怀疑np.c_,请显示这些结果(如果需要,请使用repr

print(repr(np.c_[row,ng_per_microliter]))

并跟踪dtype

适用于 v 1.12 发行说明(可能更早)

如果要转换的字符串 dtype 在“安全”转换模式下不足以容纳正在转换的整数/浮点数组的最大值,则 astype 方法现在会返回错误。以前即使结果被截断,也允许强制转换。

这可能会在进行连接时发挥作用。

【讨论】:

  • 谢谢。我应该指定这不是 Py3,而是 Python 2.7。我将进一步探索这种行为(当我可以访问我的 Ubuntu 机器时)并回来!
  • 当我使用 repr 命令时,看起来连接两个数组确实会将整个数组留在 dtype = '|S1'。有没有办法解决这个问题?
  • 我一直在检查 dtypes 的各个步骤。初始数组(例如 ng_per_microliter)是“float64”。但是当我的“填充”或“行”数组与初始数组组合时,整个 dtype 将转换为 '|S1'。
  • 所以数组包含一个字符。我们必须在版本“whats new”列表或 SO 问题中进行挖掘,以确定何时更正。在连接之前将数值数组转换为 S10 可能会有所帮助。
  • 问题系统(以及其他系统)上的 numpy 版本是什么?
【解决方案2】:

在用户 hpaulj 的帮助下,我发现这不是操作系统和环境之间行为不同的问题。这很可能是由于用户拥有不同版本的 numpy。

数组的连接会自动将 'float64' dtypes 转换为 'S1'(以匹配“填充”数组('-')和“行”数组('A'、'B' 等))。

较新版本的 numpy - 特别是 v 1.12.X - 似乎允许在没有这种自动转换的情况下串联数组。

我仍然不确定如何在旧版本的 numpy 中解决这个问题,但建议人们升级他们的版本以获得完整性能应该是一件简单的事情。 :)

【讨论】:

  • 很明显,您在这个问答中投入了大量的时间和精力。因此,我要请您花点时间查看Help Center,以了解有人回答您的问题后的指导方针。在 SO 上表示感谢的最佳方式是对您认为有用的任何问题或答案进行投票。对于您的问题,如果其中一个答案非常适合您的问题,您可以将其标记为已接受的答案。小复选标记和向上箭头是像@hpaulj 这样花这么多时间帮助我们的人获得的唯一“报酬”。
猜你喜欢
  • 2018-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多