# Variables x, y, a, b, s
x = 2         # In integer
y = 17.0      # A float 
a = [2, 3, 4] # A list of length 3
b = True      # True or false
s = 'hej'     # A string
t = (2, 4)    # A tuple. Like a list, but cannot be modified

"""
Explanation: Lecture notes from first week
Programming for the Behavioral Sciences
This week, basic concepts in Python are introducted. These concepts are important to understand the rest of the course, so make sure you understand everything presented here! The code below can be run in Spyder's iPython console.
There are many good places to get more information and examples, e.g.,
http://nbviewer.jupyter.org/github/jrjohansson/scientific-python-lectures/blob/master/Lecture-1-Introduction-to-Python-Programming.ipynb
There are many excellent introductions to Python on YouTube, so try typing "introduction to python programming" in the search field on youtube.com.
Variables and data types
A variable is used to store information. It can be 
thought of as a 'container' used to 
refer to information/data while programming
End of explanation
"""


type(x), type(y), type(a), type(b), type(s), type(t)



"""
Explanation: Note that the hashtag sign '#' is used to write comments. Text that comes after the # is ignored.
So, above we have variables of different types. What are these types and how are they different?
You can read more about data types here:
https://www.tutorialspoint.com/python/python_variable_types.htm
If we want to know the types of the above variables, the 'type' command can be used.
End of explanation
"""


%whos

"""
Explanation: We can also use the 'magic command' whos to get information about all variables currently in use
End of explanation
"""


# Let's add two variables and print the result
z = x + y
print(z)

"""
Explanation: Variable operations
Now lets try to use the above variables to understand what they do.
End of explanation
"""


a, a + a,  a * 3

# This will give you an error, since your trying to add a list a = [2, 3, 4] with a tuple t = (2, 4)
a + t

# Remember, Python starts counting from zero, x[0] refers the first element of x. x[-1] refers to the last element of x
print(t[0] + a[1])
print(t[-1], x)

"""
Explanation: Can we add other variable types as well?
End of explanation
"""


# Is this true? Notice the double equal sign for comparisons
print(1==2)
print(7==7)

print(b)
print(b is True)
print(b is not True)

"""
Explanation: How can we use boolean variables then, and what do they mean?
End of explanation
"""


l = ['a']        # make a list containing one element (a string 'a')
l.append(1)      # Append the integer 1 to the list
print(l, len(l)) # Print the list and its length

another_list =list(range(20)) # Make a list with 20 elements from 0 to 19

# Make another list with these properties
start = 10
stop = 20
step = 2
another_list_subsamples = list(range(start, stop, step))

# Convert a string to a list (rember that s='hej')
string_converted_to_list = list(s)

# Print all the above in the cell
print(another_list)
print(another_list_subsamples)
print(s)
print(string_converted_to_list)


"""
Explanation: Lists
Lists are important and general data containers used heavily in Python. A list can contant pretty much any variable type. In a list, you can mix integers, floats, strings, and other lists.
End of explanation
"""


# Let make a new string s0 = 'hi'. Let's define another one s2 = 'there'
s0 = 'hi' # You can use both single (') and double (") quotation marks to define string
s1 = 'there'

sentence0 = ' '.join([s0, s1]) # join the two strings with a space
sentence1 = ','.join([s0, s1, '!']) # join the two strings with a comma (,)

# Print the output
print(sentence0, sentence1)

# f-string example
age = 30
print(f'{s0} {s1}! I am {age} years old') #  f denotes that it's an f-string.

"""
Explanation: Strings
Strings are used to deal with text and letters. 
More info here
https://www.tutorialspoint.com/python/python_strings.htm
f-strings are also really useful to format string:
https://realpython.com/python-f-strings/
End of explanation
"""


import numpy as np  # Import numpy. Now we can use the Numpy functionalities 
                    # by its abbriviated name np (this name 'np' is arbitrary)

# Start by defining some numbers to work with
a0 = [1, 2, 3]
a1 = [4 ,5 ,6]

# What would you expect if you added a + b
c = a0 + a1
print(c, type(a0), type(a1))

"""
Explanation: Numpy arrays
Arrays are data containers for numbers. <a href="http://www.numpy.org/">NumPy</a> (numerical python) is a package for dealing with numbers and arrays. It's not part of "core" Python, so to use it, we have to start by importing it. After importing numpy, type 'np.' followed by the tab-key. Then the commands available in the NumPy package become visible in a drop-down-list.
End of explanation
"""


# Make Python understand that we're working with numbers. Convert lists to arrays
a0_array = np.array(a0)
a1_array = np.array(a1)

# No what's the output?
c0 = np.array(a0_array) + np.array(a1_array)
print(f'c0 = {c0}')

# This also works, even though we use the lists. Why?
c1 = np.add(a0, a1)
print(f'c1 = {c1}')

"""
Explanation: Oops! This is perhaps not what you expected! c is not a + b = [5, 7, 9]. Why not? Well, you used lists (try type(a0), type(a1)). We need to tell Python it should interpret the lists as arrays!
End of explanation
"""


# Generate an array with 10 random integers between 0 and 9
a = np.random.randint(0, 10, 10)
print(a)

# 'Slicing' an array means to select values from a range within the array
#The basic slice syntax is i:j:k where i is the starting index, j is the stopping index, and k is the step
a[0]    # Selects first element
a[0:2]  # Selects first two elements. Note that the value at index 2 is not taken, just values at indeces 0 and 1
a[-1]   # Selects last element
a[1:7:2]# Selects every second values from index 1 to 7
print(a[0], a[0:2], a[-1], a[1:7:2])

"""
Explanation: Let's move on to some useful examples of array generation and manipulation
End of explanation
"""


# Even more examples on how you can select elements from an array
a[np.array([0, 1, -2, -1])] # Two first and two last
a[a < 5] # Returns all values where a is less than 5
boolean_vector = a < 5
np.where(a < 5) # Positions were a < 5
values_less_than_5, positions_where_values_are_less_than_5 = a[a < 5], np.where(a < 5)
print(values_less_than_5, positions_where_values_are_less_than_5)



#  A two dimensional array (4 rows, 5 columns) with random integres between 0 and 9
v = np.random.randint(0, 10, (4 , 5))
print(v)

"""
Explanation: As you can see, this is similar to the operations we performed on the lists.
End of explanation
"""


# A very simple for-loop (the letter 'i' is often used as a counter)
n = 15 # First run this as is. Then replace 10 by n below. Test to change the value of n and see what happens.
for i in range(10):
    print(i)

"""
Explanation: For-loops
For-loops are used to repeat operations, to make computers quickly perform tasks that are repetitive and/or boring for humans. Let's start with an example you can do yourself with pen and paper: draw a square. The instructions for doing so could be (test yourself):

draw a straight line 1 unit long. Turn 90 deg right. 
draw a straight line 1 unit long. Turn 90 deg right. 
draw a straight line 1 unit long. Turn 90 deg right. 
draw a straight line 1 unit long. Turn 90 deg right. 

A more compact way of writing this would be, for instance,
repeat(4){ forward 1 unit; turn right}
Likewise, the code for drawing a staircase would be,
repeat(x){up 1 unit; turn right; forward 1 unit; turn left}
x is the number of steps in the staircase.
In Python, the syntax for a for-loop is the following.
End of explanation
"""


# loop over a letter string
letter_string = 'Programming is an important tool for researchers in behaviour sciences'

# Print every 10th letter
for letter in letter_string[slice(0, len(letter_string), 10)]:
    print(letter)

# Make list of the letters in the alphabet
alphabet = 'abcdefghijklmnopqrstuvxyz'

# first way
L1 = list(alphabet)

# Second way (using for loop)
L2 = []
for l in alphabet:
    
    # I don't like the letter x, so let's exclude it from the list
    if l != 'x': # != means NOT equal to
        L2.append(l)

print(L1, L2)

"""
Explanation: Try to explain in words to yourself what happend in the above for-loop.
End of explanation
"""


