【问题标题】:Python slice/subset array with different data type具有不同数据类型的 Python 切片/子集数组
【发布时间】:2025-12-02 23:05:02
【问题描述】:

作为模拟的结果,我有一堆按空格分隔的 csv 文件。请参见下面的示例:

Time  Node  Type  Metric 1  Metric 2
0.00   1    Abcd  1234.5678 9012.3456
0.00   1    Efgh  1234.5678 9012.3456
0.01   2    Abcd  1234.5678 9012.3456
0.01   2    Efgh  1234.5678 9012.3456
0.02   3    Abcd  1234.5678 9012.3456
0.02   3    Efgh  1234.5678 9012.3456
0.03   1    Abcd  1234.5678 9012.3456
0.03   1    Efgh  1234.5678 9012.3456
0.04   2    Abcd  1234.5678 9012.3456
0.04   2    Efgh  1234.5678 9012.3456
...

要使用指标,我需要按节点编号和类型过滤文件,即节点 1 的平均值,输入 Abcd;节点 1 的平均值,类型 Efgh;等等

我知道 Numpy 对于处理数组非常有用,但它只接受一种数据类型。我当前的代码看起来像这样(现在只是打印文件的内容):

import sys

filename = sys.argv[1]
# read file
with open(filename, 'r') as f:
    for line in f:
       print line

# TO DO
# Slice file into different 'Node' number

# Slice subfile into different 'Type'

# Calculate metrics (mean, max, min, and others)
# which is fine once I have the sliced arrays

# Plot graphs

有人知道如何有效地做到这一点吗?

PS:我使用的是 Python 2.7。

谢谢

【问题讨论】:

    标签: python arrays numpy slice


    【解决方案1】:

    您可能想使用pandas 而不是 numpy。假设你有一个制表符分隔的文件,代码会很简单:

    import pandas as pd
    data = pd.read_csv("abc.csv", delimiter="\t")
    result = data.groupby("Node").mean()
    

    并产生以下结果:

    Time    Metric 1    Metric 2
    Node            
    1   0.015   1234.5678   9012.3456
    2   0.025   1234.5678   9012.3456
    3   0.020   1234.5678   9012.3456
    

    【讨论】:

    • @ThiagoTeixeira 不客气。是的,如果你按这样的多列分组,pandas 会这样做:data.groupby(["Node", "Type"]).mean()
    • 我会试试的。谢谢!
    【解决方案2】:

    如果我将您的样本放在一个文件中,我可以将其加载到结构化的numpy 数组中

    In [45]: names=['Time','Node','Type','Metric_1','Metric_2']
    In [46]: data = np.genfromtxt('stack38285208.txt', dtype=None, names=names, skip_header=1)
    In [47]: data
    Out[47]: 
    array([(0.0, 1, b'Abcd', 1234.5678, 9012.3456),
           (0.0, 1, b'Efgh', 1234.5678, 9012.3456),
           (0.01, 2, b'Abcd', 1234.5678, 9012.3456),
           (0.01, 2, b'Efgh', 1234.5678, 9012.3456),
           (0.02, 3, b'Abcd', 1234.5678, 9012.3456),
           (0.02, 3, b'Efgh', 1234.5678, 9012.3456),
           (0.03, 1, b'Abcd', 1234.5678, 9012.3456),
           (0.03, 1, b'Efgh', 1234.5678, 9012.3456),
           (0.04, 2, b'Abcd', 1234.5678, 9012.3456),
           (0.04, 2, b'Efgh', 1234.5678, 9012.3456)], 
          dtype=[('Time', '<f8'), ('Node', '<i4'), ('Type', 'S4'), ('Metric_1', '<f8'), ('Metric_2', '<f8')])
    

    我不能使用names=True,因为您有像Metric 1 这样的名称,它会将其解释为2 个列名称。因此单独的names 列表和skip_header。我使用的是 Python3,所以S4 格式的字符串显示为b'Efgh'

    我可以通过字段名称访问字段(列),并使用它们进行各种过滤和数学运算。例如:

    Typeb'Abcd' 的字段:

    In [63]: data['Type']==b'Abcd'
    Out[63]: array([ True, False,  True, False,  True, False,  True, False,  True, False], dtype=bool)
    

    其中Node 是 1:

    In [64]: data['Node']==1
    Out[64]: array([ True,  True, False, False, False, False,  True,  True, False, False], dtype=bool)
    

    一起:

    In [65]: (data['Node']==1)&(data['Type']==b'Abcd')
    Out[65]: array([ True, False, False, False, False, False,  True, False, False, False], dtype=bool)
    In [66]: ind=(data['Node']==1)&(data['Type']==b'Abcd')
    In [67]: data[ind]
    Out[67]: 
    array([(0.0, 1, b'Abcd', 1234.5678, 9012.3456),
           (0.03, 1, b'Abcd', 1234.5678, 9012.3456)], 
          dtype=[('Time', '<f8'), ('Node', '<i4'), ('Type', 'S4'), ('Metric_1', '<f8'), ('Metric_2', '<f8')])
    

    我可以从这个记录子集中获取任何数字字段的mean

    In [68]: data[ind]['Metric_1'].mean()
    Out[68]: 1234.5678
    In [69]: data[ind]['Metric_2'].mean()
    Out[69]: 9012.3456000000006
    

    我也可以将这些字段分配给变量并直接使用它们

    In [70]: nodes=data['Node']
    In [71]: types=data['Type']
    In [72]: nodes
    Out[72]: array([1, 1, 2, 2, 3, 3, 1, 1, 2, 2])
    In [73]: types
    Out[73]: 
    array([b'Abcd', b'Efgh', b'Abcd', b'Efgh', b'Abcd', b'Efgh', b'Abcd',
           b'Efgh', b'Abcd', b'Efgh'], 
          dtype='|S4')
    

    2 个浮点字段,被视为 2 列数组:

    In [78]: metrics = data[['Metric_1','Metric_2']].view(('float',(2)))
    In [79]: metrics
    Out[79]: 
    array([[ 1234.5678,  9012.3456],
           [ 1234.5678,  9012.3456],
           [ 1234.5678,  9012.3456],
           [ 1234.5678,  9012.3456],
           [ 1234.5678,  9012.3456],
           [ 1234.5678,  9012.3456],
           [ 1234.5678,  9012.3456],
           [ 1234.5678,  9012.3456],
           [ 1234.5678,  9012.3456],
           [ 1234.5678,  9012.3456]])
    

    metrics 其中nodes 是 1

    In [83]: metrics[nodes==1,:]
    Out[83]: 
    array([[ 1234.5678,  9012.3456],
           [ 1234.5678,  9012.3456],
           [ 1234.5678,  9012.3456],
           [ 1234.5678,  9012.3456]])
    In [84]: metrics[nodes==1,:].mean(axis=0)    # column mean
    Out[84]: array([ 1234.5678,  9012.3456])
    

    numpy 没有简洁的 groupby 函数,但 Pandas 和 itertools 有。

    【讨论】:

      【解决方案3】:

      我尝试使用itertools。基本上,这利用了 groupby 方法,该方法允许您通过 lambda 函数将连续的数据块组合在一起。如果您在使用 groupby 之前对数据集进行排序,那么您基本上可以按任意键对数据集进行分组。

      不确定您的数据集有多大,但如果它不是太大,这应该可以解决问题。

      from itertools import groupby
      import sys
      
      filename = sys.argv[1]
      
      def parse_data(line):
          # converts a single entry in the csv to a list of values
          return [
                  val for val in line.split(' ') if val != ''
          ]
      
      
      with open(filename, 'r') as input:
          keys = input.readline().split()
      
          dataset = [
             parse_data(line)
             for line in input.readlines()
          ]
      
          # group dataset by node
          dataset_grouped_by_node = groupby(
              sorted(dataset, key=lambda x: x[1]), lambda x: x[1]
          )
      
          for node, node_group in dataset_grouped_by_node:
              # group each of those subgroups by type
              group_sorted_by_type = groupby(
                  sorted(node_group, key=lambda x: x[2]), lambda x: x[2]
              )
      
              for type, type_group in group_sorted_by_type:
                  print type, node
      
                  for item in type_group:
                      print item
      
                      # calculate statistics on these subgroups
      

      如果你愿意,你可以稍微清理一下以创建一个通用的“分组”功能,但我认为这应该可以满足你的需要。

      【讨论】: