【问题标题】:How can I get a list shape without using numpy?如何在不使用 numpy 的情况下获得列表形状?
【发布时间】:2018-08-22 06:21:11
【问题描述】:
a1=[1,2,3,4,5,6]  
b1=[[1,2,3], [4,5,6]]

如果使用np.shape 列表a1 将返回(6,)b1 将返回(2, 3)

如果 Numpy 被禁止,我怎样才能得到列表a1 的形状?

我主要对如何让python程序知道a1只是一维感到困惑。有什么好的方法吗?

【问题讨论】:

  • list 对象没有具有尺寸或形状。它们只有一个长度。
  • 无论如何,如果x = [[1,2,3],[4,5]]的形状应该是什么?
  • 如果列表中的每个列表都有相同数量的元素,那么“形状”将为(len(b1), len(b1[0]))
  • @grshankar 怎么样:[[[1,2],[3,4]],[[5,6],[7,8]]]?无论如何,OP 没有充分说明问题。
  • 您面临的实际问题是什么?为什么一定要“让python程序知道a1只是一维的”?

标签: python python-3.x


【解决方案1】:
>>>a = [1,2,3,4,5,6]
>>>print (len(a))
6

对于一维列表,可以使用上述方法。 len(list_name) 返回列表中元素的数量。

>>>a = [[1,2,3],[4,5,6]]
>>>nrow = len(a)
>>>ncol = len(a[0])
>>>nrow
2
>>>ncol
3

上面给出了列表的维度。 len(a) 返回行数。 len(a[0]) 返回 a[0] 中的行数,即列数。

这里是a link 原始答案。

【讨论】:

    【解决方案2】:

    问题明确指出“不使用 numpy”。但是,如果有人到达这里寻找没有任何条件的解决方案,请考虑以下内容。此解决方案适用于平衡列表。

    b1=[[1,2,3], [4,5,6]]
    np.asarray(b1).shape
    

    (2, 3)

    【讨论】:

    • 仅供参考,“从不规则的嵌套序列中创建一个 ndarray ...已被弃用。”
    【解决方案3】:

    这是解决问题的递归尝试。只有在相同深度上的所有列表都具有相同长度时,它才会起作用。否则会引发ValueError:

    from collections.abc import Sequence
    
    
    def get_shape(lst, shape=()):
        """
        returns the shape of nested lists similarly to numpy's shape.
    
        :param lst: the nested list
        :param shape: the shape up to the current recursion depth
        :return: the shape including the current depth
                (finally this will be the full depth)
        """
    
        if not isinstance(lst, Sequence):
            # base case
            return shape
    
        # peek ahead and assure all lists in the next depth
        # have the same length
        if isinstance(lst[0], Sequence):
            l = len(lst[0])
            if not all(len(item) == l for item in lst):
                msg = 'not all lists have the same length'
                raise ValueError(msg)
    
        shape += (len(lst), )
        
        # recurse
        shape = get_shape(lst[0], shape)
    
        return shape
    

    根据您的输入(以及来自 cmets 的输入),结果如下:

    a1=[1,2,3,4,5,6]
    b1=[[1,2,3],[4,5,6]]
    
    print(get_shape(a1))  # (6,)
    print(get_shape(b1))  # (2, 3)
    print(get_shape([[0,1], [2,3,4]]))  # raises ValueError
    print(get_shape([[[1,2],[3,4]],[[5,6],[7,8]]]))  # (2, 2, 2)
    

    不确定最后的结果是不是你想要的。


    更新

    正如mkl 在 cmets 中指出的那样,上面的代码不会捕获嵌套列表的形状不一致的所有情况;例如[[0, 1], [2, [3, 4]]] 不会引发错误。

    这是一个检查形状是否一致的镜头(可能有更有效的方法来做到这一点......)

    from collections.abc import Sequence, Iterator
    from itertools import tee, chain
    
    def is_shape_consistent(lst: Iterator):
        """
        check if all the elements of a nested list have the same
        shape.
    
        first check the 'top level' of the given lst, then flatten
        it by one level and recursively check that.
    
        :param lst:
        :return:
        """
    
        lst0, lst1 = tee(lst, 2)
    
        try:
            item0 = next(lst0)
        except StopIteration:
            return True
        is_seq = isinstance(item0, Sequence)
    
        if not all(is_seq == isinstance(item, Sequence) for item in lst0):
            return False
    
        if not is_seq:
            return True
    
        return is_shape_consistent(chain(*lst1))
    

    可以这样使用:

    lst0 = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
    lst1 = [[0, 1, 2], [3, [4, 5]], [7, [8, 9]]]
    
    assert is_shape_consistent(iter(lst0))
    assert not is_shape_consistent(iter(lst1))
    

    【讨论】:

    • 请注意,检查 if not all(len(item) == l for item in lst): raise 不会捕获所有不一致的地方。例如,get_shape([[0, 1], [2, [3, 4]]]) 返回(2, 2),但[[0, 1], [2, [3, 4]]] 没有明确定义的形状。
    • @mkl 你可以额外计算嵌套列表中元素的总数,并检查它是否与 shape 中条目的乘积相匹配...
    • 如果你选择[[0]],它的形状是(1, 1),你只需要计算“死角”,即-list元素。但是你的条件在[[0, 1, 2], [3, [4, 5]], [7, [8, 9]]] 上失败了,它有 9 个元素和“形状”(3, 3)
    • @mkl 是的,正如我写的那样,我认为必须有可能构建一个无视我的简单检查的列表......
    • @mkl 添加了一个应该能够验证形状是否一致的函数。没有经过很好的测试......它还能被愚弄吗?你有什么更高效的方法吗?
    【解决方案4】:

    这是来自 Joel Grus 的 "Ten Essays on Fizz Buzz" 书中使用递归的一个很好的例子。

    from typing import List, Tuple, Union
    
    
    def shape(ndarray: Union[List, float]) -> Tuple[int, ...]:
        if isinstance(ndarray, list):
            # More dimensions, so make a recursive call
            outermost_size = len(ndarray)
            row_shape = shape(ndarray[0])
            return (outermost_size, *row_shape)
        else:
            # No more dimensions, so we're done
            return ()
    

    例子:

    three_d = [
        [[0, 0, 0], [1, 1, 1], [2, 2, 2]],
        [[0, 0, 0], [1, 1, 1], [2, 2, 2]],
        [[0, 0, 0], [1, 1, 1], [2, 2, 2]],
        [[0, 0, 0], [1, 1, 1], [2, 2, 2]],
        [[0, 0, 0], [1, 1, 1], [2, 2, 2]],
    ]
    
    result = shape(three_d)
    print(result)
    >>> (5, 3, 3)
    

    【讨论】:

      【解决方案5】:

      根据所需的彻底程度,我建议使用尾递归。从最里面到最外面的列表构建形状。这将允许您检查所有尺寸在每个深度和索引处是否匹配。

      def shape(lst):
          def ishape(lst):
              shapes = [ishape(x) if isinstance(x, list) else [] for x in lst]
              shape = shapes[0]
              if shapes.count(shape) != len(shapes):
                  raise ValueError('Ragged list')
              shape.append(len(lst))
              return shape
          return tuple(reversed(ishape(lst)))
      

      这是 IDEOne 上的演示:https://ideone.com/HJRwlC

      shapes.count(shape) != len(shapes) 是一个巧妙的技巧,用于确定给定级别的所有形状是否相同,取自https://stackoverflow.com/a/3844948/2988730

      如果您的唯一目标是确定列表是否是一维的,只需在最外层列表上运行一个 all

      is_1d = all(not isinstance(x, list) for x in lst)
      

      is_1d = not any(isinstance(x, list) for x in lst)
      

      【讨论】:

        【解决方案6】:

        以下函数跟踪列表每个维度的第一项。不管它有多少维度。

        def list_shape(input):
            
        shape = []
            a = len(input)
            shape.append(a)
            b = input[0]
        
            while a > 0:
                try:
        
                    a = len(b)
                    shape.append(a)
                    b = b[0]
        
                except:
                    break
                
            return shape
        
        list1 = [[[123], [231]], [[345], [231]]]
        
        print(list_shape(list1))
        

        输出:

        [2, 2, 1]
        

        注意:仅适用于对称列表和列表中的数字数据。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-06-08
          • 1970-01-01
          • 1970-01-01
          • 2016-01-23
          • 1970-01-01
          • 1970-01-01
          • 2017-06-08
          • 1970-01-01
          相关资源
          最近更新 更多