Scripting 5.7. Vectors and matrices

1. What is a vector?

A numeric vector is an array of numbers, regarded as a single object. For instance, the squares of the first five integers can be collected in the vector { 1, 4, 9, 16, 25 }. In a Praat script, you can put a vector into a variable whose name ends in a number sign (“#”):

    squares# = { 1, 4, 9, 16, 25 }

After this, the variable squares# contains the value { 1, 4, 9, 16, 25 }. We say that the vector squares# has five dimensions, i.e. it contains five numbers.

Whereas in Scripting 3.2. Numeric variables we talked about a numeric variable as being analogous to a house where somebody (the numeric value) could live, a numeric vector with five dimensions can be seen as a street that contains five houses, which are numbered with the indexes 1, 2, 3, 4 and 5, each house containing a numeric value. Thus, the street squares# contains the following five houses: squares# [1], squares# [2], squares# [3], squares# [4] and squares# [5]. Their values (the numbers that currently live in these houses) are 1, 4, 9, 16 and 25, respectively.

To list the five values with a loop, you could do:

    writeInfoLine: “Some squares:”
    for i from 1 to size (squares#)
       appendInfoLine: “The square of ”, i, “ is ”, squares# [i]
    endfor

Instead of the above procedure to get the vector squares#, with a pre-computed list of five squares, you could compute the five values with a formula, as in the example of Scripting 5.6. Arrays and dictionaries. However, in order to put a value into an element of the vector, you have to create the vector first (i.e., you have to build the whole street before you can put something in a house), so we start by creating a vector with five zeroes in it:

    squares# = zero# (5)

After this, squares# is the vector { 0, 0, 0, 0, 0 }, i.e., the value of each element is zero. Now that the vector (street) exists, we can put values into (populate) the five elements (houses):

    for i from 1 to size (squares#)
       squares# [i] = i * i
    #endfor

After this, the variable squares# has the value { 1, 4, 9, 16, 25 }, as before, but now we had the computer compute the squares.

2. Creating a vector

You can create a vector in many ways. The first way we saw was with a vector literal, i.e. a series of numbers (or numeric formulas) between braces:

    lengths# = { 1.83, 1.795, 1.76 }

The second way we saw was to create a series of zeroes. To create a vector consisting of 10,000 zeroes, you do

    zero# (10000)

Another important type of vector is a series of random numbers. To create a vector consisting of 10,000 values drawn from a Gaussian distribution with true mean 0.0 and true standard deviation 1.0, you could do

    noise# = randomGauss# (10000, 0.0, 1.0)

To create a vector consisting of 10,000 values drawn from a uniform distribution of real numbers with true minimum 0.0 and true maximum 1.0, you use

    randomUniform# (10000, 0.0, 1.0)

To create a vector consisting of 10,000 values drawn from a uniform distribution of integer numbers with true minimum 1 and true maximum 10, you use

    randomInteger# (10000, 1, 10)

To create a vector containing the integer numbers 1 through 64, you use

    to# (64)

To create a vector containing the integer numbers 10 through 20, you use

    from_to# (10, 20)

To create a vector containing linearly increasing (not necessarily integer) numbers from 10 through 20 in steps of 2, you use

    from_to_by# (10, 20, 2)

To create five linearly increasing numbers between 0 and 10 (i.e. { 0, 2.5, 5, 7.5, 10 }), you use

    from_to_count# (0, 10, 5)

To divide the range between 0 and 12 symmetrically with step 5 (i.e. { 1, 6, 11 }), you use

    between_by# (0, 12, 5)

To divide the range between 0 and 10 into five equal parts and list their centres (i.e. { 1, 3, 5, 7, 9 }), you use

    between_count# (0, 10, 5)

To sort the numbers in a vector (e.g. { 7.4, 1.3, 3.6 }), you use

    sort# ({ 7.4, 1.3, 3.6 })

which yields { 1.3, 3.6, 7.4 }.

To randomly shuffle the numbers in a vector (e.g. { 7.4, 1.3, 3.6 }), you use

    shuffle# ({ 7.4, 1.3, 3.6 })

which can yield { 1.3, 7.4, 3.6 } or any of the five other orders of the elements.

Vectors can also be created by some menu commands. For instance, to get vectors representing the times and pitch frequencies of the frames in a Pitch object, you can do

    selectObject: myPitch
    times# = List all frame times
    pitches# = List values in all frames: “Hertz”

3. Turning a vector into a number

For the vector defined above, you can compute the sum of the five values as

    sum (squares#)

which gives 55. You compute the average of the five values as

    mean (squares#)

which gives 11. You compute the standard deviation of the values as

    stdev (squares#)

which gives 9.669539802906858 (the standard deviation is undefined for vectors with fewer than 2 elements). The center of gravity of the distribution defined by regarding the five values as relative frequencies as a function of the index from 1 to 5 is computed by

    center (squares#)

which gives 4.090909090909091 (for a vector with five elements, the result will always be a number between 1.0 and 5.0). You compute the inner product of two equally long vectors as follows:

    other# = { 2, 1.5, 1, 0.5, 0 }
    result = inner (squares#, other#)

which gives 1*2 + 4*1.5 + 9*1 + 16*0.5 + 25*0 = 25. The formula for this is ∑i=15 squares[i] * other[i], so that an alternative piece of code could be

    result = sumOver (i to 5, squares# [i] * other# [i])

4. Converting vectors to vectors

    a# = squares# + 5 ; adding a number to each element of a vector

causes a# to become the vector { 6, 9, 14, 21, 30 }.

    b# = a# + { 3.14, 2.72, 3.16, -1, 7.5 } ; adding two vectors of the same length

causes b# to become the vector { 9.14, 11.72, 17.16, 20, 37.5 }.

    c# = b# / 2 ; dividing each element of a vector

causes c# to become the vector { 4.57, 5.86, 8.58, 10, 18.75 }.

    d# = b# * c# ; elementwise multiplication

causes d# to become the vector { 41.7698, 68.6792, 147.2328, 200, 703.125 }.

A vector can also be given to a menu command that returns another vector. For instance, to get a vector representing the pitch frequencies at 0.01-second intervals in a Pitch object, you can do

    selectObject: myPitch
    tmin = Get start time
    tmax = Get end time
    times# = between_by# (tmin, tmax, 0.01)
    pitches# = List values at times: times#, "hertz", "linear"

5. What is a matrix?

A numeric matrix is a two-indexed array of numbers, regarded as a single object. In a Praat script, you can put a matrix into a variable whose name ends in two number signs (“##”):

    confusion## = {{ 3, 6, 2 }, { 8, 2, 1 }}

After this, the variable confusion## contains the value {{ 3, 6, 2 }, { 8, 2, 1 }}. We say that the matrix confusion## has two rows and three columns, i.e. it contains six numbers.

Whereas a numeric vector with five dimensions could be seen (see above) as a street that contains five houses, the matrix confusion## can be seen as a city district with two avenues crossed by three streets, where everybody lives on an intersection (the analogies start to get less realistic).

6. Creating a matrix

You can create a matrix in many ways. The first way we saw was with a matrix literal, i.e. a series of series of numbers (or numeric formulas) between nested braces.

The second way is as a matrix of zeroes. To create a matrix consisting of 100 rows of 10,000 zeroes, you do

    a## = zero## (100, 10000)

After this,

    numberOfRows (a##)

is 100, and

    numberOfColumns (a##)

is 10000.

Another important type of matrix is one filled with random numbers. To create a matrix consisting of 100 rows of 10,000 values drawn from a Gaussian distribution with true mean 0.0 and true standard deviation 1.0, you can do

    noise## = randomGauss## (100, 10000, 0.0, 1.0)

You can create a matrix as the outer product of two vectors:

    m## = outer## (u#, v#)

which is the same as

    m## = zeros## (size (u#), size (v#))
    for irow to size (u#)
       for icol to size (v#)
          m## [irow, icol] = u# [irow] * v# [icol]
       endfor
    endfor

or in mathematical notation

mij = ui vj (i = 1..M, j = 1..N)

where M is the number of rows and N is the number of columns.

7. Computations with matrices

You can add matrices:

    c## = a## + b##

Elementwise multiplication:

    c## = a## * b##

which does

cij = aij bij (i = 1..M, j = 1..N)

Matrix multiplication:

    c## = mul## (a##, b##)

which does

mij = ∑k=1K aik bkj (i = 1..M, j = 1..N)

where M is the number of rows of a, N is the number of columns of b, and K is the number of columns of a, which has to be equal to the number if rows of b.

Matrix-by-vector multiplication:

    v# = mul# (m##, u#)

which does

vi = ∑j=1N mij uj (i = 1..M)

where M is the number of rows of m, and N is the number of columns of m, which has to be equal to the dimension of u. Also

    v# = mul# (u#, m##)

which does

vj = ∑i=1M ui mij (j = 1..N)

where M is the number of rows of m, which has to be equal to the dimension of u, and N is the number of columns of m.

8. String vectors

You can create string vectors in the following ways:

    a$# = { "hello", "goodbye" }

creates a vector with two strings, which you can access as a$# [1], which is “hello”, and a$# [2], which is “goodbye”.

    a$# = empty$# (10)

creates a vector with 10 empty strings, which you can access as a$# [1] through a$# [10].

    text$# = readLinesFromFile$# ("hello.txt")

creates a vector with 100 strings if the file hello.text contains 100 lines of text.

    fileNames$# = fileNames$# ("sound/*.wav")

creates a vector containing the names of all WAV files in the folder sound.

    folderNames$# = folderNames$# (".")

creates a vector containing the names of all folders in the folder where the script resides.

    inks$# = splitByWhitespace$# ("Hello, how are you?")

creates a vector containing the strings “Hello,” (including the comma), “how”, “are”, and “you?”.

Links to this page


© ppgb 20230130