# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # Function Node
#
# Satra once called the `Function` module, the "do anything you want card". Which is a perfect description. Because it allows you to put any code you want into an empty node, which you than can put in your workflow exactly where it needs to be.
#
# You might have already seen the `Function` module in the [example section in the Node tutorial](basic_nodes.ipynb#Example-of-a-simple-node). Let's take a closer look at it again.

# +
# Import Node and Function module
from nipype import Node, Function

# Create a small example function
def add_two(x_input):
    return x_input + 2

# Create Node
addtwo = Node(Function(input_names=["x_input"],
                       output_names=["val_output"],
                       function=add_two),
              name='add_node')

addtwo.inputs.x_input =4
addtwo.run()
addtwo.result.outputs
# -

# # Trap 1
#
# There are only two traps that you should be aware when you're using the `Function` module. The first one is about naming the input variables. The variable name for the node input has to be the exactly the same name as the function input parameter, in this case this is `x_input`. 
#
# Otherwise you get the following error:
#
#     TypeError: add_two() got an unexpected keyword argument 'x_input'
#     Interface Function failed to run.
#     
# **Note** that in the current version of `Nipype` you don't have to provide `input_names` as an argument of `Function`.

# # Trap 2
#
# If you want to use another module inside a function, you have to import it again inside the function. Let's take a look at the following example:

# +
from nipype import Node, Function

# Create the Function object
def get_random_array(array_shape):

    # Import random function
    from numpy.random import random
   
    return random(array_shape)

# Create Function Node that executes get_random_array
rndArray = Node(Function(input_names=["array_shape"],
                         output_names=["random_array"],
                         function=get_random_array),
                name='rndArray_node')

# Specify the array_shape of the random array
rndArray.inputs.array_shape = (3, 3)

# Run node
rndArray.run()

# Print output
print(rndArray.result.outputs)
# -

# Now, let's see what happens if we move the import of `random` outside the scope of `get_random_array`:

# +
from nipype import Node, Function

# Import random function
from numpy.random import random


# Create the Function object
def get_random_array(array_shape):
  
    return random(array_shape)

# Create Function Node that executes get_random_array
rndArray = Node(Function(input_names=["array_shape"],
                         output_names=["random_array"],
                         function=get_random_array),
                name='rndArray_node')

# Specify the array_shape of the random array
rndArray.inputs.array_shape = (3, 3)

# Run node
rndArray.run()

# Print output
print(rndArray.result.outputs)
# -

# As you can see, if we don't import `random` inside the scope of the function, we receive the following error:
#
#     NameError: global name 'random' is not defined
#     Interface Function failed to run. 

StarcoderdataJupyterScriptsDedupFiltered