Getting ready for JuliaCon Local Eindhoven 2023

By: Blog by Bogumił Kamiński

Re-posted from: https://bkamins.github.io/julialang/2023/10/13/juliacon.html

Introduction

I am really excited that soon we will have a Julia conference in Europe.
The JuliaCon Local Eindhoven 2023 will take place on December 1, 2023 in Eindhoven.

I thought that as nice preparation for this event it would be useful to share
some tips that can be useful for DataFrames.jl users.
Frequently, when working with tables, one has to process time series data.
I want to share a few examples how such data can be stored in DataFrames.jl.

The post was written under Julia 1.9.2 and DataFrames.jl 1.6.1.

Setting up the stage

Let us start with a data frame that has 4 columns and 1000 rows representing consecutive measurements of some data.
I also add the :period column keeping track of the moment at which the measurement was made.

julia> using DataFrames

julia> using Random

julia> Random.seed!(1234);

julia> df1 = DataFrame(rand(1000, 4), :auto);

julia> df1.period = 1:1000;

julia> df1
1000×5 DataFrame
  Row │ x1         x2         x3        x4         period
      │ Float64    Float64    Float64   Float64    Int64
──────┼───────────────────────────────────────────────────
    1 │ 0.579862   0.473374   0.366288  0.771849        1
    2 │ 0.411294   0.176997   0.652475  0.938484        2
    3 │ 0.972136   0.676856   0.900155  0.390673        3
    4 │ 0.0149088  0.177963   0.374975  0.694063        4
  ⋮   │     ⋮          ⋮         ⋮          ⋮        ⋮
  997 │ 0.424295   0.78012    0.577681  0.274228      997
  998 │ 0.481554   0.0441882  0.700181  0.317677      998
  999 │ 0.401528   0.658903   0.438309  0.211217      999
 1000 │ 0.58985    0.909857   0.354735  0.442829     1000
                                          992 rows omitted

Reshaping data: wide to long

The first operation we want to do is reshaping the data from wide to long format,
in which we will have three columns: the period, the variable name, and the variable value.
To achieve this we can use the stack function:

julia> df2 = stack(df1, Not(:period))
4000×3 DataFrame
  Row │ period  variable  value
      │ Int64   String    Float64
──────┼─────────────────────────────
    1 │      1  x1        0.579862
    2 │      2  x1        0.411294
    3 │      3  x1        0.972136
    4 │      4  x1        0.0149088
  ⋮   │   ⋮        ⋮          ⋮
 3997 │    997  x4        0.274228
 3998 │    998  x4        0.317677
 3999 │    999  x4        0.211217
 4000 │   1000  x4        0.442829
                   3992 rows omitted

Reshaping data: long to wide

Now let us perform the reverse operation, but this time put :period as columns.
We can do it using the unstack function:

julia> df3 = unstack(df2, :variable, :period, :value)
4×1001 DataFrame
 Row │ variable  1         2         3         4          5         6        ⋯
     │ String    Float64?  Float64?  Float64?  Float64?   Float64?  Float64? ⋯
─────┼────────────────────────────────────────────────────────────────────────
   1 │ x1        0.579862  0.411294  0.972136  0.0149088  0.520355  0.639562 ⋯
   2 │ x2        0.473374  0.176997  0.676856  0.177963   0.670122  0.042361
   3 │ x3        0.366288  0.652475  0.900155  0.374975   0.387857  0.779594
   4 │ x4        0.771849  0.938484  0.390673  0.694063   0.724383  0.130453
                                                           995 columns omitted

Nesting columns

Another way to reshape this data is to put all periods in a single cell of a data frame.
We can do it in two ways.

The first is to keep them in columns :x1, :x2, :x3, and :x4. Here are two ways
how you can do it starting from df1 or df2:

julia> combine(df1, Not(:period) .=> Ref, renamecols=false)
1×4 DataFrame
 Row │ x1                                 x2                                 ⋯
     │ Array…                             Array…                             ⋯
─────┼────────────────────────────────────────────────────────────────────────
   1 │ [0.579862, 0.411294, 0.972136, 0…  [0.473374, 0.176997, 0.676856, 0…  ⋯
                                                             2 columns omitted
julia> unstack(df2, [], :variable, :value, combine=collect)
1×4 DataFrame
 Row │ x1                                 x2                                 ⋯
     │ Array…?                            Array…?                            ⋯
─────┼────────────────────────────────────────────────────────────────────────
   1 │ [0.579862, 0.411294, 0.972136, 0…  [0.473374, 0.176997, 0.676856, 0…  ⋯
                                                             2 columns omitted

Alternatively, we could want to have two columns, the first with variable names
and the second with the vectors. Again, let me show two ways to do it starting
from df2 and df3 data frames:

julia> df4 = combine(groupby(df2, :variable), :value => Ref, renamecols=false)
4×2 DataFrame
 Row │ variable  value
     │ String    SubArray…
─────┼─────────────────────────────────────────────
   1 │ x1        [0.579862, 0.411294, 0.972136, 0…
   2 │ x2        [0.473374, 0.176997, 0.676856, 0…
   3 │ x3        [0.366288, 0.652475, 0.900155, 0…
   4 │ x4        [0.771849, 0.938484, 0.390673, 0…

julia> combine(df3, :variable, AsTable(Not(:variable)) => ByRow(identity∘collect) => :value)
4×2 DataFrame
 Row │ variable  value
     │ String    Array…
─────┼─────────────────────────────────────────────
   1 │ x1        Union{Missing, Float64}[0.579862…
   2 │ x2        Union{Missing, Float64}[0.473374…
   3 │ x3        Union{Missing, Float64}[0.366288…
   4 │ x4        Union{Missing, Float64}[0.771849…

These examples are slightly more complicated so let me explain them.

In the first one we use Ref to protect a vector against expansion into multiple columns
(note that no copying of data happens here, to perform a copy you should write Ref∘copy).

In the second one the AsTable(Not(:variable)) source column selector produces a NamedTuple.
However, Julia users probably are aware that with 1000 entries such a NamedTuple would take a long
time to compile. For this reason we are using an optimization that DataFrames.jl provides.
If we pass a function of a form some_function∘collect then this compilation step is avoided.
In our case the function is just identity as we are happy with producing a vector (which collect already returns).

Unnesting columns

As a last example let us show how to expand the df4 data frame back into multiple columns.
This is easy. Just write:

julia> select(df4, :variable, :value => AsTable)
4×1001 DataFrame
 Row │ variable  x1        x2        x3        x4         x5        x6       ⋯
     │ String    Float64   Float64   Float64   Float64    Float64   Float64  ⋯
─────┼────────────────────────────────────────────────────────────────────────
   1 │ x1        0.579862  0.411294  0.972136  0.0149088  0.520355  0.639562 ⋯
   2 │ x2        0.473374  0.176997  0.676856  0.177963   0.670122  0.042361
   3 │ x3        0.366288  0.652475  0.900155  0.374975   0.387857  0.779594
   4 │ x4        0.771849  0.938484  0.390673  0.694063   0.724383  0.130453
                                                           995 columns omitted

Since we used AsTable as target column names specifier we got auto-generated column names.

Conclusions

I hope you liked the examples I presented today and learned something from them.

I am sure that if you attend JuliaCon Local Eindhoven 2023 you are going to
see a lot of highly informative and inspirational talks there!

Don’t forget about GPT-4

By: Logan Kilpatrick

Re-posted from: https://logankilpatrick.medium.com/dont-forget-about-gpt-4-d5ab8c9493fc?source=rss-2c8aac9051d3------2

Exploring the model that changed the path of AI and machine learning history

Image created by Author and DALL-E 3

The age of powerful language-based AI is upon us, and few players compare to the might and potential of OpenAI’s GPT-4. Let’s delve into the intricacies, capabilities, and potential applications of this revolutionary language model.

Picture the Power of GPT-4

Image captured from source video [1]

GPT-4 has truly broken barriers with its ability to generate up to 25,000 words of text, a monumental increase of about eight times compared to its predecessor, chat GPT. This leap forward enhances GPT-4’s abilities in handling long passages of text, making it a significant tool for a range of applications requiring long-duration interactions or wide-spanning narratives.

Advanced Image Understanding

Image captured from source video [1]

GPT-4’s advance into understanding, interpreting, and coherently describing images revolutionizes the idea of automated systems. Imagine snapping a picture of a scene, uploading it to GPT-4, and having the AI describe the visual elements perfectly. The idea that an AI can not only “see” an image but also make sense of different elements and predict outcomes, like explaining that cutting the strings of balloons would make them fly away, is fascinatingly next-gen.

GPT-4’s ability to understand images makes it an invaluable assistant in several fields — from virtual education to diverse areas where describing visuals in word processing is required.

Unique Challenges and Improvements

Image captured from source video [1]

Like any technology, AI language models come with their challenges, including adversarial usage, unwanted content, and privacy concerns. However, OpenAI has put substantial effort into mitigating these issues. With GPT-4, the team has implemented further measures for safety, alignment, and usefulness to make the model more user-friendly and secure.

Groundbreaking Applications in Education

GPT-4’s potential in revolutionizing education is immense. Imagine enriching every classroom with a personal AI tutor capable of addressing questions on a wide range of subjects. Or a fifth-grader getting unlimited time for personalized math tutoring with this AI that never gets tired or impatient. GPT-4 makes tailor-made tutoring accessible to all, directly in the comfort of their homes.

Image captured from source video [1]

Ultimately, GPT-4 elevates everyday life through advancements in AI. Whether it’s boosting productivity, teaching new skills, or simply organizing our days, AI like GPT-4 stands to ameliorate our lives in countless ways.

Shaping the Future of AI with Microsoft

The strategic partnership between OpenAI and Microsoft is aimed at transforming AI technology into useful tools accessible to everyone. Their concerted efforts lay the groundwork for harnessing AI’s full potential to enhance productivity, ultimately leading to an improved quality of life. GPT-4, a product born from the convergence of numerous technology advances, holds incredible promise for the future.

From enhancing education with AI-powered tutors to bringing valuable assistance into our lives, GPT-4 is on the verge of redefining our interactions with technology. As with any tool, ensuring that AI serves us correctly and safely is essential to leverage its benefits fully. As we sculpt the future of AI, learning, updating, improving, and transparency stand as our guiding tenets.

As we eagerly anticipate wider access to GPT-4 and similar AI, it’s critical to approach this revolutionary technology with informed understanding and responsible usage. OpenAI’s breakthrough serves as a testament to humanity’s unyielding prowess to innovate and evolve, even in the realms of artificial intelligence. Happy coding!

Source video [1]: https://www.youtube.com/watch?v=–khbXchTeE

Note: This blog post was generated by a GPT-4 pipeline as part of a demo for the AI Engineer Summit presentation in collaboration with Simon Posada Fishman.

Explore the Capabilities of Broadcasting in Julia Programming

By: Steven Whitaker

Re-posted from: https://glcs.hashnode.dev/broadcasting

Julia is a relatively new,free, and open-source programming language.It has a syntaxsimilar to that of other popular programming languagessuch as MATLAB and Python,but it boasts being able to achieve C-like speeds.

Unlike other languagesthat focus on technical computing,Julia does not require usersto vectorize their code(i.e., to have one version of a functionthat operates on scalar valuesand another versionthat operates on arrays).Instead,Julia provides a built-in mechanismfor vectorizing functions:broadcasting.

Broadcasting is useful in Juliafor several reasons,including:

  • It allows functionsthat operate on scalar values(e.g., cos())to operate elementwiseon an array of values,eliminating the needfor specialized, vectorized versionsof those functions.
  • It allows for more efficient memory allocationin certain situations.For example,suppose we have a function, func,and we want to computefunc(1, 2) and func(1, 3).Instead of broadcastingon [1, 1] and [2, 3],we can broadcaston 1 and [2, 3],avoiding the memory allocationfor [1, 1].

On top of that,Julia provides a very convenient syntaxfor broadcasting,making it so anyonecan easily use broadcasting in their code.

In this post,we will learn what broadcasting is,and we will see several examplesof how to effectively use broadcasting.

This post assumes you already have Julia installed.If you haven’t yet,check out our earlierpost on how to install Julia.

What Is Broadcasting?

Broadcasting essentially is a methodfor calling functions elementwisewhile virtually copying inputsso that all inputs have the same size.(For example,if two inputs to a broadcasted function fare 1 and [1, 2, 3],the first input is treatedas if it is [1, 1, 1]but without actually allocating memoryfor an array.Then the function is appliedto each pair of inputs:f(1, 1), f(1, 2), and f(1, 3).)

If that definition doesn’t make sense right now,don’t worry,the examples below will help illustrate.

The Dot Syntax

The first thing to know about broadcastingis that it is very convenient to use.

All you need to do is add dots.

For example,if you want to take the square rootof a collection of values,just add a dot (.):

julia> sqrt.([1, 4, 9]) # Notice the dot after `sqrt`3-element Vector{Float64}: 1.0 2.0 3.0

Vectorizing Operators and Functions

As stated earlier,Julia doesn’t requirevectorized versions of functions.In fact,many functions don’t even have methodsthat take array inputs.Take sqrt for example:

julia> sqrt([1, 4, 9]) # No dot after `sqrt`ERROR: MethodError: no method matching sqrt(::Vector{Int64})

So, even though sqrtdoesn’t have a vectorized versionexplicitly defined,the dot syntax still allowssqrt to be applied elementwise.The same applies to other functions and operators:

julia> [1, 2, 3] ^ 3 # No dotERROR: MethodError: no method matching ^(::Vector{Int64}, ::Int64)julia> [1, 2, 3] .^ 3 # With dot3-element Vector{Int64}:  1  8 27julia> uppercase(["hello", "world"]) # No dotERROR: MethodError: no method matching uppercase(::Vector{String})julia> uppercase.(["hello", "world"]) # With dot2-element Vector{String}: "HELLO" "WORLD"

Vectorization

Vectorization even workswith user-defined functions:

julia> myfunc(x) = x * 2myfunc (generic function with 1 method)julia> myfunc.([1, 2])2-element Vector{Int64}: 2 4

Note that some functionsdo have methodsthat operate on arrays,so be careful when decidingwhether a function should apply elementwise.Take cos as an example:

julia> A = [0 ; /2 /6];julia> cos(A) # Matrix cosine, *not* elementwise cosine2x2 Matrix{Float64}: -0.572989  -0.285823 -0.142912  -0.620626julia> cos.(A) # Add a dot for computing the cosine elementwise2x2 Matrix{Float64}: 1.0          -1.0 6.12323e-17   0.866025

Broadcasting with Multiple Inputs

Broadcasting gets more interestingwhen multiple inputs are involved.Let’s use addition (+) as an example.

We can add a scalar to each element of an array:

julia> [1, 2, 3] .+ 103-element Vector{Int64}: 11 12 13julia> 10 .+ [1, 2, 3]3-element Vector{Int64}: 11 12 13

Scalar-vector broadcasting

We can also sum two arrays elementwise:

julia> [1, 2, 3] .+ [10, 20, 30]3-element Vector{Int64}: 11 22 33

Broadcasting even works with arraysof different sizes.The only requirement is that non-singleton dimensionsmust match across inputs.

julia> [1 2 3; 4 5 6] .+ [10, 20] # Sizes: (2, 3) and (2,)2x3 Matrix{Int64}: 11  12  13 24  25  26julia> [1 2 3; 4 5 6] .+ [10 20] # Sizes: (2, 3) and (1, 2)ERROR: DimensionMismatch: arrays could not be broadcast to a common size; got a dimension with lengths 3 and 2julia> [1 2 3; 4 5 6] .+ [10 20 30] # Sizes: (2, 3) and (1, 3)2x3 Matrix{Int64}: 11  22  33 14  25  36

In the first example([1 2 3; 4 5 6] .+ [10, 20]),the column vector [10, 20]was added to each columnof the matrix,while in the second working example([1 2 3; 4 5 6] .+ [10 20 30]),the row vector [10 20 30]was added to each rowof the matrix.

Matrix-vector broadcasting

Matrix-row-vector broadcasting

Treating Inputs as Scalars

Sometimes,it is usefulto broadcast overonly a subset of the inputs.For example,suppose we have a functionthat scales an input matrix:

julia> myfunc2(X, a) = X * amyfunc2 (generic function with 1 method)

Suppose we want to scale a given matrixby several different scale factors.The result should be an array of matrices,one matrix for each scale factor applied.We might try to use broadcasting:

julia> X = [1 2; 3 4]; a = [10, 20];julia> myfunc2.(X, a)2x2 Matrix{Int64}: 10  20 60  80

But the result is just one matrix!We have one matrix becausewe broadcasted over a and X,not just a.In this case,we need to wrap Xin a single-element Tuple:

julia> myfunc2.((X,), a)2-element Vector{Matrix{Int64}}: [10 20; 30 40] [20 40; 60 80]

Now we have the result we want:an array where the first entryis X scaled by a[1]and the second entryis X scaled by a[2].

So,whenever you need to treat an inputas a scalarfor broadcasting purposes,just wrap it in a Tuple.

Broadcasting with Dictionaries and Strings

Dictionaries and stringsmay act differently than expectedin broadcasting,so let’s clarify some things here.

First,attempting to broadcast over a dictionarywill throw an error:

julia> d = Dict("key1" => "hello", "key2" => "world")Dict{String, String} with 2 entries:  "key2" => "world"  "key1" => "hello"julia> println.(d)ERROR: ArgumentError: broadcasting over dictionaries and `NamedTuple`s is reserved

There are different solutionsdepending on the context.For example:

  • Treat the dictionary as a scalar:
    julia> println.((d,)); # Note that `d` is wrapped in a `Tuple`Dict("key2" => "world", "key1" => "hello")
  • Broadcast over the values explicitly:
    julia> println.(values(d));worldhello

Regarding strings,strings are treated as scalars,not as collections of characters.For example:

julia> string.("string", [1, 2])2-element Vector{String}: "string1" "string2"

(The above would have erroredif strings were not treated as scalars,because length("string") is 6,whereas length([1, 2]) is 2.)

To broadcast over the charactersin a string,use collect:

julia> string.(collect("string"), 1:6)6-element Vector{String}: "s1" "t2" "r3" "i4" "n5" "g6"

Summary

In this post,we learned what broadcasting is,and we saw several examplesof how to effectively use broadcastingto apply functions elementwise.

Have any further questions about broadcasting?Feel free to ask themin the comments below!

Does broadcasting make sense now?Move on to thenext post to learn about Julia’s type system!Or,feel free to take a lookat our other Julia tutorial posts!

Additional Links