mooreryan

mooreryan

Access behaviour for Explorer.DataFrame

I have a couple of questions about Explorer’s DataFrame and the Access behaviour.

In the selecting columns section of the Explorer manual, it mentions that the Access behaviour is implemented for DataFrames.

Because of this I would expect that accessing a column that doesn’t exist with the bracket notation to return nil and using DataFrame.fetch to try an access a column that doesn’t exist to return :error.

However, this is not the case. Here are some examples.

Examples

df =
  DataFrame.new(
    a: [1, 2, 3],
    b: [10, 20, 30]
  )

And here are some ways to access it.

Using brackets

iex> df["a"]
#Explorer.Series<
  Polars[3]
  s64 [1, 2, 3]

iex> df["c"]
** (ArgumentError) could not find column name "c". The available columns are: ["a", "b"].
If you are attempting to interpolate a value, use ^c.

Trying to access column c raises an ArgumentError, but I would expect that to return nil given the Access behaviour.

Using fetch

Using fetch was also surprising:

iex> Explorer.DataFrame.fetch(df, "a")
{:ok,
 #Explorer.Series<
   Polars[3]
   s64 [1, 2, 3]
 >}

iex> Explorer.DataFrame.fetch(df, "c")
** (ArgumentError) could not find column name "c". The available columns are: ["a", "b"].
If you are attempting to interpolate a value, use ^c.

The first makes sense ({:ok, val}) but the second, raises an ArgumentError, where I would expect it to return :error given the Access behaviour.

I assume that this is the intended way for it to work given that it is included in the test suite. See this test for example, which shows that an ArgumentError is expected to be raised.

(The fact that it is an ArgumentError is also interesting given that fetch!/2 from Access raises a KeyError exception rather than ArgumentError.)

Other Access behaviour functions

Another interesting thing is that not all of the functions in the Access behaviour are available. E.g.,

iex> Explorer.DataFrame.fetch!(df, "c")
** (UndefinedFunctionError) function Explorer.DataFrame.fetch!/2 is undefined or private.

Summary

To summarize, here are my questions:

  • Why does accessing columns in a DataFrame not behave in the way that the Access behaviour docs imply that it should behave?
  • Why aren’t all the Access behaviour functions available on DataFrame?
  • How are you supposed to check if a column exists in a DataFrame other than using a try block?

Marked As Solved

josevalim

josevalim

Creator of Elixir

The Explorer team has decided that it makes more sense to raise (catching errors early) than return nil. How much this is violation of the Access behaviour is a good question. Here is what we can say:

  1. Keyword lists are used to model optional keys, but they raise if the key is not an atom
  2. Maps always return nil for missing keys, but they also have a convenience API for being strict, such as map.foo
  3. Nx tensors raise if you use access with an invalid dimension

I’d have to put more thought into it but, in the face of the two conflicting positions below, I’d probably stick with the first one:

  • We should let data structures decide what is best for them
  • Access should impose it to return nil

The Access behaviour requires you only to implement fetch and get_and_update, the other functionality is made available through the Access module itself. It is similar to a GenServer, where you implement handle_call but you must invoke GenServer.call.

We should add an API for it.

Also Liked

billylanchantin

billylanchantin

You can also do:

df = Explorer.DataFrame.new(a: [1, 2, 3])

if "a" in df.names do
  # ...
end

It’s not perfect because Access is more inclusive. For example:

# Both of these work
df["a"] #=> #Explorer.Series<Polars[3] s64 [1, 2, 3]>
df[:a]  #=> #Explorer.Series<Polars[3] s64 [1, 2, 3]>

# Only the string version works
"a" in df.names #=> true
:a in df.names  #=> false

So it’s a bit of a leaky abstraction. But depending on what you’re doing it may be good enough.

Last Post!

mooreryan

mooreryan

Thanks for the responses josevalim and billylanchantin!!

That makes sense. I was not aware that the return types for a behaviour were flexible.

This is interesting. I’m new to Elixir, so I can’t really say how it ought to be, but at least for me, the DataFrame implantation of Access being different from the Access docs was surprising. But that could be addressed by adding something to DataFrame docs.

Thanks for the explanation. That makes sense.

Thanks, I will go with something like this for now.

Where Next?

Popular in Questions Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

Other popular topics Top

jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40042 209
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 44139 214
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

We're in Beta

About us Mission Statement