寻求测试复杂Numpy运算的标准或更优方法
Great question—tracking how elements move through complex NumPy operations like outer products and einsum can be really opaque with numerical arrays, and it’s frustrating that string arrays don’t play nice with ufuncs. Here are a few practical, standard approaches to make this easier:
1. Custom String Operations for Index Rearrangement Tasks
For operations where you just need to track element origins (like einsum, reshaping, or slicing), you can work around ufunc limitations by creating custom string-aware functions. Since einsum only rearranges elements (not performing arithmetic), it works perfectly with string arrays once you’ve built your intermediate array correctly.
For your example:
import numpy as np # Create your character arrays a_str = np.array(list("abcdefghi")).reshape(3,3) b_str = np.array(list("ABCDEFGHI")).reshape(3,3) # Custom outer concatenation (since np.add.outer doesn't support strings) # We expand dimensions to broadcast the arrays correctly c_str = np.char.add(a_str[:, :, np.newaxis, np.newaxis], b_str[np.newaxis, np.newaxis, :, :]) # Now apply einsum just like you would with numerical arrays d_str = np.einsum('kjij->ijk', c_str) # Check the result print(d_str[0,1,2]) # Outputs 'bH' (a_str[0,1] + b_str[2,1])
This gives you the exact label correspondence you wanted, and it’s straightforward for tasks where you’re just moving elements around.
2. Symbolic Arrays with SymPy
For more complex operations that involve actual arithmetic (not just rearrangement), SymPy’s symbolic arrays let you track the exact mathematical origin of each element. This is especially useful if you need to verify both the structure and the algebraic logic of your transformations.
Example:
import sympy as sp import numpy as np # Create symbolic variables for each element of a and b a_sym = sp.Matrix(list("abcdefghi")).reshape(3,3) b_sym = sp.Matrix(list("ABCDEFGHI")).reshape(3,3) # Convert to NumPy arrays of SymPy symbols (for easier outer product handling) a_np = np.array(a_sym.tolist()) b_np = np.array(b_sym.tolist()) # Compute outer product (symbols are preserved) c_sym = np.multiply.outer(a_np, b_np) # Apply einsum d_sym = np.einsum('kjij->ijk', c_sym) # Check an element print(d_sym[0,1,2]) # Outputs b*H (showing exactly which elements multiplied)
This not only shows you the element correspondence but also preserves the operation (multiply in this case), so you can validate that your einsum is applying the right logic.
3. Index Tracking with Integer Arrays
If you need to work with numerical operations but still want to trace element origins, use integer arrays where each value represents the index of the original element. You can then map these indices back to labels after processing.
Example:
import numpy as np # Create label mappings labels_a = np.array(list("abcdefghi")).reshape(3,3) labels_b = np.array(list("ABCDEFGHI")).reshape(3,3) # Create index arrays (each element is its flat index in the original array) idx_a = np.arange(9).reshape(3,3) idx_b = np.arange(9).reshape(3,3) # Compute outer product of indices (tracks which a and b indices combine) c_idx = np.stack([np.expand_dims(idx_a, (2,3)), np.expand_dims(idx_b, (0,1))], axis=-1) # Apply einsum to the index array d_idx = np.einsum('kjij->ijk', c_idx) # Map indices back to labels d_labels = np.char.add(labels_a.flat[d_idx[...,0]], labels_b.flat[d_idx[...,1]]) print(d_labels[0,1,2]) # Outputs 'bH'
This approach is flexible for any type of operation (arithmetic or rearrangement) because you’re tracking indices rather than values directly.
All three methods solve the problem of losing track of element origins during chained transformations—pick the one that best fits whether you need to verify string correspondence, algebraic logic, or numerical index flow.
内容的提问来源于stack exchange,提问作者Schiphol

