FancyArray Examples

The FancyArray class in the fancypy library provides a robust and flexible way to handle array data with enhanced functionality. This documentation covers the usage of the FancyArray class, including initialization, array manipulation, and array analysis.

Array Initialization

Creating a FancyArray

To create an instance of a FancyArray, simply initialize it with the necessary data. FancyArray can handle different types of data and provide various functionalities out of the box.

import numpy as np
from numpy.typing import NDArray

from power_grid_model_ds.fancypy import FancyArray


class FancyTestArray(FancyArray):
    """Test array with some attributes"""

    id: NDArray[np.int_]
    test_int: NDArray[np.int_]
    test_float: NDArray[np.float64]
    test_str: NDArray[np.str_]
    test_bool: NDArray[np.bool_]


fancy_array = FancyTestArray(
    id=[1, 2, 3],
    test_int=[3, 0, 4],
    test_float=[4.0, 4.0, 1.0],
    test_str=["a", "c", "d"],
    test_bool=[True, False, True],
)

This creates a FancyTestArray instance with integer, float, boolean and string columns.

Array Manipulation

Accessing Array Elements

You can access the elements of a FancyArray using standard indexing. The class supports both single and multiple column access.

# Accessing a single column
id_column = fancy_array["id"]
id_column = fancy_array.id

# Accessing multiple columns
subset = fancy_array[["id", "test_int"]]

Setting Attributes

Attributes of the FancyArray can be dynamically set and modified. Changes are reflected in the underlying data.

# Setting new values to a column
fancy_array.id = np.array([9, 9, 9])
fancy_array["id"] = np.array([9, 9, 9])
fancy_array.id = [9, 9, 9]
fancy_array["id"] = [9, 9, 9]
fancy_array.id = 9
fancy_array["id"] = 9

Preventing Deletion of Numpy Attributes

Certain attributes inherent to numpy arrays, like size, cannot be deleted from a FancyArray.

# Attempting to delete a numpy attribute raises an error
try:
    del fancy_array.size
except AttributeError as e:
    print(e)
'FancyTestArray' object has no attribute 'size'

Array Analysis

Iterating Over Rows You can iterate over the rows of a FancyArray, and each row is also an instance of FancyArray.

for row in fancy_array:
    print(row)
 id | test_int | test_float | test_str | test_bool 
 9  |    3     |    4.0     |    a     |    True   
 id | test_int | test_float | test_str | test_bool 
 9  |    0     |    4.0     |    c     |   False   
 id | test_int | test_float | test_str | test_bool 
 9  |    4     |    1.0     |    d     |    True   

Analyzing Data

The FancyArray class allows for detailed data analysis. You can use various numpy functions and methods to perform analysis on the data.

# Example analysis: sum of a column
total = np.sum(fancy_array.test_int)

Handling Non-Existing Attributes

Attempting to access a non-existing attribute will raise an AttributeError.

try:
    value = fancy_array.non_existing_attribute
except AttributeError as e:
    print(e)
'numpy.ndarray' object has no attribute 'non_existing_attribute'

Subclassing FancyArray

You can create subclasses of FancyArray to add specific functionality or constraints, such as custom string lengths.

class CustomStrLengthArray(FancyArray):
    test_str: NDArray[np.str_]
    _str_lengths = {"test_str": 100}


custom_array = CustomStrLengthArray(test_str=["a" * 100])

Using fancypy

The fancypy library provides additional tools and utilities to enhance the functionality of FancyArray. These tools help with various operations such as data manipulation, validation, and transformation.

Importing fancypy

To use the functionalities provided by fancypy, you need to import the library along with FancyArray.

import numpy as np

from power_grid_model_ds import fancypy as fp
from power_grid_model_ds.fancypy import FancyArray

Example Functions

concatenate

The concatenate function combines multiple FancyArray instances into a single array. This is useful for merging datasets.

class FancyTestArray(FancyArray):
    """Test array with some attributes"""

    id: NDArray[np.int_]
    value: NDArray[np.int_]


# Creating two FancyTestArray instances
array1 = FancyTestArray(
    id=[1, 2],
    value=[10, 20],
)
array2 = FancyTestArray(
    id=[3, 4],
    value=[30, 40],
)

# Concatenating the arrays
concatenated_array = fp.concatenate(array1, array2)
print(concatenated_array)
 id | value 
 1  |   10  
 2  |   20  
 3  |   30  
 4  |   40  

unique

The unique function returns the unique elements of a FancyArray along a specified axis. This is similar to numpy’s unique function but tailored for FancyArray.

# Creating a FancyTestArray with duplicate values
fancy_array = FancyTestArray(
    id=[1, 2, 2, 3],
    value=[10, 20, 20, 30],
)


# Getting unique elements
unique_array = fp.unique(fancy_array, axis=0)
print(unique_array)
 id | value 
 1  |   10  
 2  |   20  
 3  |   30  

sort

The sort function sorts the elements of a FancyArray along a specified axis.

# Creating a FancyArray with unsorted values
fancy_array = FancyTestArray(
    id=[3, 1, 2],
    value=[30, 10, 20],
)

# Sorting the array by the 'id' column
sorted_array = fp.sort(fancy_array, axis=0)
print(sorted_array)
 id | value 
 1  |   10  
 2  |   20  
 3  |   30  

array_equal

The array_equal function checks whether two FancyArray instances are element-wise equal. This is useful for comparing datasets.

# Creating two FancyTestArray instances for comparison
array1 = FancyTestArray(
    id=[1, 2, 3],
    value=[10, 20, 30],
)
array2 = FancyTestArray(
    id=[1, 2, 3],
    value=[10, 20, 30],
)

# Checking if the arrays are equal
are_equal = fp.array_equal(array1, array2)
print(f"Are the arrays equal? {are_equal}")
Are the arrays equal? True

Using Filters

The FancyArray class provides various filtering capabilities to manipulate and analyze array data efficiently. This section covers how to use the filtering functions such as exclude, filter, and get based on the provided test files.

Excluding Elements

Exclude by ID

The exclude method allows you to remove elements from the array based on specified criteria. You can exclude elements by their ID.

# Exclude elements with ID 1
excluded_array = fancy_array.exclude(id=1)

Exclude by Value

You can also exclude elements based on the value of a specific attribute.

# Exclude elements where 'value' is 10
excluded_array = fancy_array.exclude(value=10)
print(excluded_array)
 id | value 
 2  |   20  
 3  |   30  

Exclude with Multiple Conditions

The exclude method supports logical operations to combine multiple conditions. By default, it uses the AND operation, but you can specify the OR operation using the mode_ parameter.

# Exclude elements where 'id'=1 or 'value'=20
excluded_array = fancy_array.exclude(id=1, value=20, mode_="OR")
print(excluded_array)
 id | value 
 3  |   30  

Filtering Elements

Filter by ID

The filter method allows you to select elements from the array based on specified criteria. Filtering by ID can be done as follows:

# Filter elements with ID 1
filtered_array = fancy_array.filter(id=1)

Filter by Value

Similar to the exclude method, you can filter elements based on the value of a specific attribute.

# Filter elements where 'value' is 20
filtered_array = fancy_array.filter(value=20)

Filter with Multiple Conditions

The filter method also supports logical operations for combining multiple conditions.

# Filter elements where 'id' is 1 or 'value' is 20
filtered_array = fancy_array.filter(id=1, value=20, mode_="OR")
print(filtered_array)
 id | value 
 1  |   10  
 2  |   20  

Getting Elements

Get by ID

The get method retrieves a single element from the array that matches the specified criteria. You can get elements by their ID.

# Get element with ID 1
element = fancy_array.get(id=1)

Get by Value

You can also retrieve elements based on the value of a specific attribute.

# Get element where 'value' is 20
element = fancy_array.get(value=20)
print(element)
 id | value 
 2  |   20  

Handling Multiple Matches

If the get method finds multiple elements that match the criteria, it raises a MultipleRecordsReturned exception. If no elements match, it raises a RecordDoesNotExist exception.

# Handling multiple matches
from power_grid_model_ds.errors import MultipleRecordsReturned, RecordDoesNotExist

fancy_array = FancyTestArray(
    id=[1, 2, 3, 4],
    value=[10, 20, 20, 30],
)

try:
    element = fancy_array.get(value=20)
except MultipleRecordsReturned:
    print("Multiple records found")

# Handling no matches
try:
    element = fancy_array.get(value=99.0)
except RecordDoesNotExist:
    print("No record found")
Multiple records found
No record found