NumPy: Conceitos Centrais do ndarray

Última atualização: 2026-08-26

A estrutura de dados central do NumPy é o ndarray — o array N-dimensional. Ele se parece com uma lista Python, mas por baixo dos panos é completamente diferente: tipo fixo, memória contígua, operações vetorizadas. Entender os detalhes internos do ndarray é o primeiro passo para usar o NumPy efetivamente.

1. O que Você Aprenderá



2. Uma História Real de um Analista de Dados

(1) O Problema: Excel em Python, Confusão

Bob tem uma tabela Excel de 3 linhas e 4 colunas. Ele a armazena como uma lista Python de listas — 3 sublistas aninhadas dentro de uma lista externa. Ele quer somar a segunda coluna, então escreve um loop for para iterar por cada linha. Para 1 milhão de linhas, isso leva 5 segundos. Pior, quando uma string se infiltra em uma das linhas, o loop não falha até chegar àquela célula.

(2) A Solução ndarray

Charlie sugere que Bob converta a tabela para um ndarray com np.array. NumPy achata os dados em um bloco contíguo de memória e realiza a soma da coluna em uma operação vetorizada em nível C — as mesmas 1 milhão de linhas levam apenas 5 milissegundos. Os tipos são verificados no momento da criação, então uma string errante é capturada imediatamente, não no meio da computação.

(3) O Resultado



3. A Essência do ndarray

(1) Tipos de Dados Homogêneos

Todos os elementos de um ndarray devem ser do mesmo tipo. Isso é fundamentalmente diferente das listas Python — listas podem misturar inteiros, strings e até objetos, enquanto ndarray permite apenas um dtype.

PYTHON
import numpy as np

a = np.array([1, 2, 3])
print(a.dtype)  # int64

b = np.array([1.0, 2.0, 3.0])
print(b.dtype)  # float64

c = np.array([1, 2.0, 3])
print(c.dtype)  # float64 (promoção automática)
TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.
⚠️ Nota: O código abaixo precisa ser executado em um ambiente Python local.

(2) Armazenamento em Memória Contígua

Listas Python armazenam um array de ponteiros — cada ponteiro aponta para um PyObject separado espalhado pela memória heap. ndarray, por outro lado, organiza todos os dados em um único bloco contíguo de memória, sem sobrecarga de ponteiros e excelente eficiência de cache da CPU.

Característica Lista Python ndarray
Tipo de elemento Qualquer mistura Mesmo dtype
Armazenamento Array de ponteiros → PyObjects espalhados Bloco de memória contíguo
Sobrecarga por elemento 28+ bytes (cabeçalho PyObject) Dependente do dtype (1/2/4/8 bytes)
Cache-friendly Ruim (salto de ponteiros) Excelente (acesso sequencial)
Segurança de tipo Nenhuma (capturada em tempo de execução) Verificada na criação

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.

: Propriedades de arrays 0D/1D/2D/3D (Dificuldade ⭐)

PYTHON
import numpy as np

a0 = np.array(42)                   # 0D escalar
a1 = np.array([1, 2, 3])            # 1D
a2 = np.array([[1, 2], [3, 4]])     # 2D
a3 = np.array([[[1, 2], [3, 4]],
               [[5, 6], [7, 8]]])   # 3D

for name, arr in [("0D", a0), ("1D", a1), ("2D", a2), ("3D", a3)]:
    print(f"{name}: ndim={arr.ndim}, shape={arr.shape}, size={arr.size}")
# 0D: ndim=0, shape=(), size=1
# 1D: ndim=1, shape=(3,), size=3
# 2D: ndim=2, shape=(2,2), size=4
# 3D: ndim=3, shape=(2,2,2), size=8
TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.
⚠️ Nota: O código abaixo precisa ser executado em um ambiente Python local.



4. Dimensões, Shape e Size

(1) ndim / shape / size

Atributo Significado Exemplo (shape=(3,4))
ndim Número de dimensões (eixos) 2
shape Comprimento de cada eixo, como tupla (3, 4)
size Número total de elementos = produto dos comprimentos dos eixos 12
PYTHON
import numpy as np

a = np.zeros((3, 4))
print(a.ndim)    # 2
print(a.shape)   # (3, 4)
print(a.size)    # 12
TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.
⚠️ Nota: O código abaixo precisa ser executado em um ambiente Python local.

(2) Strides

strides indica quantos bytes pular na memória para mover um passo ao longo de um dado eixo.

Atributo Significado shape=(3,4) dtype=int64
strides Stride por eixo (bytes) (32, 8)
itemsize Bytes por elemento 8
PYTHON
import numpy as np

a = np.zeros((3, 4), dtype=np.int64)
print(a.strides)   # (32, 8)
# eixo 0: mover 1 linha = 4 * 8 = 32 bytes
# eixo 1: mover 1 coluna = 1 * 8 = 8 bytes
TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.
⚠️ Nota: O código abaixo precisa ser executado em um ambiente Python local.

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.

: Visualização de strides (Dificuldade ⭐⭐)

PYTHON
import numpy as np

a = np.arange(12, dtype=np.int32).reshape(3, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]
print(f"shape:   {a.shape}")    # (3, 4)
print(f"strides: {a.strides}")  # (16, 4)
print(f"itemsize: {a.itemsize}")  # 4
# stride do eixo 0: 4 elementos * 4 bytes = 16
# stride do eixo 1: 1 elemento  * 4 bytes = 4
TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.
⚠️ Nota: O código abaixo precisa ser executado em um ambiente Python local.



5. Intuição Por Trás dos Eixos

Eixos são a chave para entender operações com ndarray. axis=0 é a dimensão mais externa, axis=1 é a próxima, e assim por diante.

Eixo Posição no shape Intuição 2D shape=(3,4) 3D shape=(2,3,4)
axis=0 Mais externo Direção da linha (através das linhas) Ao longo do eixo "camada"
axis=1 Segundo Direção da coluna (através das colunas) Ao longo do eixo "linha"
axis=2 Mais interno Ao longo do eixo "coluna"

Dica de memorização: axis=k corresponde à direção de shape[k]. Fazer sum/reduce ao longo desse eixo "elimina" aquela dimensão.

PYTHON
import numpy as np

a = np.arange(24).reshape(2, 3, 4)
print(a.shape)         # (2, 3, 4)
print(a.sum(axis=0).shape)  # (3, 4) — eixo 0 eliminado
print(a.sum(axis=1).shape)  # (2, 4) — eixo 1 eliminado
print(a.sum(axis=2).shape)  # (2, 3) — eixo 2 eliminado
TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.
⚠️ Nota: O código abaixo precisa ser executado em um ambiente Python local.



6. dtype e Uso de Memória

(1) Sistema de Tipos dtype

dtype Descrição Bytes Intervalo
np.int8 Inteiro com sinal de 8 bits 1 -128 ~ 127
np.int32 Inteiro com sinal de 32 bits 4 -2^31 ~ 2^31-1
np.int64 Inteiro com sinal de 64 bits 8 -2^63 ~ 2^63-1
np.float32 Ponto flutuante de 32 bits 4 ~±3.4e38, precisão de 7 dígitos
np.float64 Ponto flutuante de 64 bits 8 ~±1.8e308, precisão de 15 dígitos
np.bool_ Booleano 1 True/False
np.complex128 Complexo de 128 bits 16 Dois float64

(2) Modelo de Memória: ndarray Não É uma Lista Aninhada

100%
graph TB
    A["Objeto ndarray"] --> B["ponteiro de dados<br/>bloco de memória bruta"]
    A --> C["dtype<br/>tipo de elemento"]
    A --> D["shape<br/>(3, 4)"]
    A --> E["strides<br/>(32, 8)"]
    B --> F["0,0 | 0,1 | 0,2 | 0,3 | 1,0 | 1,1 | ... | 2,3"]
    style A fill:#4CAF50,color:#fff
    style B fill:#2196F3,color:#fff
    style F fill:#FF9800,color:#fff
TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.

(3) Tabela de Referência Shape-Memória

shape ndim size dtype=float64 nbytes strides
(10,) 1 10 80 (8,)
(3, 4) 2 12 96 (32, 8)
(2, 3, 4) 3 24 192 (96, 32, 8)
(5, 2, 3, 4) 4 120 960 (192, 96, 32, 8)

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.

: Comparação de memória por dtype (Dificuldade ⭐)

PYTHON
import numpy as np

for dtype in [np.int8, np.int32, np.int64, np.float32, np.float64]:
    a = np.zeros(1000, dtype=dtype)
    print(f"{dtype.__name__:10s} itemsize={a.itemsize}  nbytes={a.nbytes}")
# int8       itemsize=1  nbytes=1000
# int32      itemsize=4  nbytes=4000
# int64      itemsize=8  nbytes=8000
# float32    itemsize=4  nbytes=4000
# float64    itemsize=8  nbytes=8000
TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.
⚠️ Nota: O código abaixo precisa ser executado em um ambiente Python local.

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.

: Verificar que reshape não copia dados (Dificuldade ⭐⭐)

PYTHON
import numpy as np

a = np.arange(12, dtype=np.int64)
b = a.reshape(3, 4)

print(a.shape, b.shape)       # (12,) (3, 4)
print(a.strides, b.strides)   # (8,) (32, 8)
print(a.nbytes, b.nbytes)     # 96 96

b[0, 0] = 999
print(a[0])  # 999 — mesma memória!
TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.
⚠️ Nota: O código abaixo precisa ser executado em um ambiente Python local.



7. Exemplo Abrangente: Análise Profunda de Array 3D

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.

: Análise completa de propriedades de um array 3×4×2 (Dificuldade ⭐⭐⭐)

PYTHON
import numpy as np

a = np.arange(24, dtype=np.int64).reshape(3, 4, 2)

print("=== Array completo ===")
print(a)
# [[[ 0  1]
#   [ 2  3]
#   [ 4  5]
#   [ 6  7]]
#  [[ 8  9]
#   [10 11]
#   [12 13]
#   [14 15]]
#  [[16 17]
#   [18 19]
#   [20 21]
#   [22 23]]]

print(f"ndim:    {a.ndim}")     # 3
print(f"shape:   {a.shape}")    # (3, 4, 2)
print(f"size:    {a.size}")     # 24
print(f"dtype:   {a.dtype}")    # int64
print(f"itemsize:{a.itemsize}") # 8
print(f"nbytes:  {a.nbytes}")   # 192  (24 * 8)
print(f"strides: {a.strides}")  # (64, 16, 8)
# eixo 0: 4*2*8 = 64 bytes por passo
# eixo 1: 2*8   = 16 bytes por passo
# eixo 2: 1*8   = 8  bytes por passo

# Verificar strides manualmente
expected_strides = (
    a.shape[1] * a.shape[2] * a.itemsize,  # 4*2*8 = 64
    a.shape[2] * a.itemsize,                # 2*8   = 16
    a.itemsize                               # 8
)
print(f"manual strides: {expected_strides}")  # (64, 16, 8)
print(f"match: {a.strides == expected_strides}")  # True

# Redução de eixo
print(a.sum(axis=0).shape)  # (4, 2)
print(a.sum(axis=1).shape)  # (3, 2)
print(a.sum(axis=2).shape)  # (3, 4)
TEXT 📖 Somente leitura
> **Saída:** Execute NumPy 2.x no seu ambiente Python local para ver a saída do ndarray. O servidor Piston não tem NumPy pré-instalado — instale localmente (`pip install numpy`) e compare. Os valores reais podem variar conforme a versão do NumPy e a semente aleatória.
⚠️ Nota: O código abaixo precisa ser executado em um ambiente Python local.



❓ Perguntas Frequentes

P: O ndarray pode armazenar tipos diferentes? R: Não. ndarray é homogêneo — todos os elementos devem compartilhar o mesmo dtype. Se você tentar misturar tipos, NumPy fará uma promoção automática (ex.: int → float). Tipos incompatíveis gerarão um erro.

P: Qual é a linha — axis=0 ou axis=1? R: Em um array 2D, axis=0 é a direção da linha (através das linhas) e axis=1 é a direção da coluna (através das colunas). Somar ao longo de axis=0 "comprime as linhas em uma linha"; somar ao longo de axis=1 "comprime as colunas em uma coluna".

P: O que são strides? R: Strides é uma tupla indicando quantos bytes pular na memória para mover um passo ao longo de cada eixo. Eles são a chave para como ndarray pode fazer reshape/transposição sem copiar dados.

P: Qual a diferença entre float64 e float32? R: float64 ocupa 8 bytes com ~15 dígitos decimais de precisão; float32 ocupa 4 bytes com ~7 dígitos. float32 usa metade da memória, mas tem precisão menor. Aprendizado profundo comumente usa float32, enquanto computação científica tipicamente usa float64.

P: Por que ndarray é chamado de "homogêneo"? R: Porque todos os elementos são empacotados de forma contígua na memória, cada um ocupando o mesmo número de bytes e interpretados da mesma forma (determinada pelo dtype compartilhado). Isso permite que a CPU processe múltiplos elementos de uma vez com instruções vetorizadas — a razão fundamental do alto desempenho do NumPy.


📖 Resumo



📝 Exercícios

  1. Básico (Dificuldade ⭐): Crie quatro arrays de comprimento 5000 com dtypes int8, int32, float32 e float64. Imprima o nbytes de cada array e explique por que diferentes dtypes usam diferentes quantidades de memória.

  2. Intermediário (Dificuldade ⭐⭐): Dado um ndarray com shape=(5, 3, 2) e dtype=int16, calcule seus strides e nbytes manualmente, então verifique seu cálculo com código.

  3. Desafio (Dificuldade ⭐⭐⭐): Explique com suas próprias palavras como um ndarray com shape=(2,3,4) é disposto na memória. Desenhe um diagrama, rotule os strides e mostre o deslocamento de memória para cada elemento.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%