Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Dataframe docs update #693

Merged
merged 10 commits into from
Jan 3, 2025
248 changes: 178 additions & 70 deletions docs/framework/dataframe.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,97 +2,205 @@
title: "Dataframe"
---

**writer framework places the dataframe at the core of the application**. This is a great way for modeling a complex and massive data system.
it offers components as `dataframe` to manipulate dataframes. These components allow you to visualize and interact with dataframes.
**Writer Framework has two objects with the name _DataFrame_:**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add an intro sentence here to help people understand what they're trying to accomplish with dataframes?


1. **Code Dataframe:** In code that you write for a Writer Framework application, `DataFrame` refers to a **_data structure_** that stores data in rows and columns in a way similar to a spreadsheet or SQL table.
2. **UI DataFrame:** In Writer Framework's UI, "DataFrame" refers to a **_user interface component_** that displays data in rows and columns in a way similar to a spreadsheet or SQL table.

To present a data table in Writer Framework, you create a `DataFrame` data structure in your code and then bind it to a UI Dataframe.


## Displaying a static DataFrame
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please briefly discuss the options in the DataFrame component (e.g. sorting, downloading, searching) and link to the component documentation.


A static DataFrame is one whose content does not change. The user can change its sort order, but the data within the DataFrame remains constant.

<Steps>
<Step title="Create a DataFrame data structure">
Writer Framework supports both [pandas](https://pandas.pydata.org/) and [Polars](https://pola.rs/) `DataFrame` data structures. Create a `DataFrame`, assign its value to a variable, then assign make that variable a value in the `state` dictionary:

<CodeGroup>
```sh pandas
AccordionGuy marked this conversation as resolved.
Show resolved Hide resolved
import writer as wf
import pandas as pd

data = [
{"rank": 1, "flavor": "Vanilla", "favorite": 0.11},
{"rank": 2, "flavor": "Chocolate", "favorite": 0.1},
{"rank": 3, "flavor": "Cookies and cream", "favorite": 0.07},
{"rank": 4, "flavor": "Strawberry", "favorite": 0.06},
{"rank": 5, "flavor": "Chocolate chip", "favorite": 0.02},
]
df = pd.DataFrame(data)

wf.init_state({
"mydf": df
})
```

```sh Polars
AccordionGuy marked this conversation as resolved.
Show resolved Hide resolved
import writer as wf
import polars as pl

data = [
{"rank": 1, "flavor": "Vanilla", "favorite": 0.11},
{"rank": 2, "flavor": "Chocolate", "favorite": 0.1},
{"rank": 3, "flavor": "Cookies and cream", "favorite": 0.07},
{"rank": 4, "flavor": "Strawberry", "favorite": 0.06},
{"rank": 5, "flavor": "Chocolate chip", "favorite": 0.02},
]
df = pl.DataFrame(data)

wf.init_state({
"mydf": df
})
```
</CodeGroup>

The call to `wf.init_state()` adds the `DataFrame` to the application's `state` variable as the value of the `mydf` key.
</Step>
<Step title="Add a DataFrame component to the UI and bind it to the DataFrame data structure">
Add a DataFrame UI component to the user interface, then set its **Data** property to `@{`_dataframe_key_`}`, where _dataframe_key_ is the `state` variable key whose value refers to the `DataFrame` data structure.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The underscores at the front and end of _dataframe_key_ add italics and break the code formatting, I'd remove them.


In the case of this example, `mydf` is the `state` variable key referring to the `DataFrame`, so set the **Data** property to `@{mydf}`.

![DataFrame for static table example with properties panel open](images/dataframe_static_table_1.png)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This image is broken for me, might want to double check the URL against other docs. I think you need to add /framework to the front possibly but check out other examples.

</Step>
</Steps>


## Displaying an editable DataFrame

A editable DataFrame is one whose content can change. Like static DataFrames, editable DataFrames use the **DataFrame** UI component. Unlike static tables, the DataFrame UI component is bound to an instance of `EditableDataframe`, a class provided by the Writer library. Changes to a `EditableDataframe` object will be immediately reflected in the DataFrame UI component that it is bound to.

<Note>Note that the "f" in `EditableDataframe` is _not_ capitalized — it’s `EditableDataframe`, not `EditableDataFrame`!</Note>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FabienArcellier @madeindjs can we change to standard capitalization of DataFrame? (I find it weird too, but it is what it is)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samjulien @ajot @AccordionGuy in general, if you find yourself writing something like this, please provide feedback and we'll likely fix it on our end 😀

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we can. I have used the class name of pandas. Dataframe or dataframe looks better.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you'll change from EditableDataframe to EditableDataFrame?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, bad reading for my comment on github. Ok for me to go to EditableDataFrame.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AccordionGuy we'll fix this on our end, can you please change docs to EditableDataFrame and remove the <Note>?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ramedina86 @FabienArcellier I've made the updates as requested and will let you folks know if I find potential issues like the capitalization of EditableDataframe — I figured that there might be a good reason for the original capitalization, but I’ll ask next time!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @AccordionGuy 🙏🏻


<Steps>
<Step title="Create an EditableDataframe data structure">
An `EditableDataframe` object can be instantiated from any of the following:

1. A pandas `DataFrame`
2. A Polars `DataFrame`
3. A list of dictionaries

<CodeGroup>
```sh pandas
AccordionGuy marked this conversation as resolved.
Show resolved Hide resolved
import writer as wf
import pandas as pd

data = [
{"rank": 1, "flavor": "Vanilla", "favorite": 0.11},
{"rank": 2, "flavor": "Chocolate", "favorite": 0.1},
{"rank": 3, "flavor": "Cookies and cream", "favorite": 0.07},
{"rank": 4, "flavor": "Strawberry", "favorite": 0.06},
{"rank": 5, "flavor": "Chocolate chip", "favorite": 0.02},
]
df = pd.DataFrame(data)

wf.init_state({
"mydf": wf.EditableDataframe(df)
})
```

```sh Polars
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
```sh Polars
```python Polars

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

☝️ ➕

import writer as wf
import polars as pl

data = [
{"rank": 1, "flavor": "Vanilla", "favorite": 0.11},
{"rank": 2, "flavor": "Chocolate", "favorite": 0.1},
{"rank": 3, "flavor": "Cookies and cream", "favorite": 0.07},
{"rank": 4, "flavor": "Strawberry", "favorite": 0.06},
{"rank": 5, "flavor": "Chocolate chip", "favorite": 0.02},
]
df = pl.DataFrame(data)

wf.init_state({
"mydf": wf.EditableDataframe(df)
})
```

```sh List of dictionaries
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
```sh List of dictionaries
```python List of dictionaries

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

☝️ ➕

import writer as wf

data = [
{"rank": 1, "flavor": "Vanilla", "favorite": 0.11},
{"rank": 2, "flavor": "Chocolate", "favorite": 0.1},
{"rank": 3, "flavor": "Cookies and cream", "favorite": 0.07},
{"rank": 4, "flavor": "Strawberry", "favorite": 0.06},
{"rank": 5, "flavor": "Chocolate chip", "favorite": 0.02},
]

wf.init_state({
"mydf": wf.EditableDataframe(data)
})
```
</CodeGroup>

The call to `wf.init_state()` adds the `DataFrame` to the application's `state` variable as the value of the `mydf` key.
</Step>
<Step title="Add a DataFrame component to the UI and bind it to the DataFrame data structure">
Add a **DataFrame** component to the user interface, then set its **Data** property to `@{`_dataframe_key_`}`, where _dataframe_key_ is the `state` variable key whose value refers to the `DataFrame` data structure.

In the case of this example, `mydf` is the `state` variable key referring to the `DataFrame`, so set the **Data** property to `@{mydf}`.

![DataFrame for dynamic table example with properties panel open](images/dataframe_dynamic_table_1.png)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto on image URL being broken

</Step>
</Steps>

## Updating an editable DataFrame

Editable DataFrames are updated by updating the `EditableDataframe` object they are bound to, which is done using `EditableDataframe`'s methods.

### `record_add`: Add a new row

`record_add()` adds a new row to an `EditableDataframe`. It takes a dictionary with the following structure...

| compatibility | dataframe |
|--------------------|---------------------------------------|
| `pandas.DataFrame` | x |
| `polar.DataFrame` | x |
| `list of records` | x (with `EditableDataframe`) |
```python
{"record": new_row}
```

## Use a dataframe
...where `new_row` is a dictionary containing the data for the row to be added.

**a dataframe is simply added to the state**. A component like `dataframe` will be able to display it.
In the code example above, you would add a new row to the DataFrame with the following code:

```python
import pandas
import writer as wf

wf.init_state({
'mydf': pandas.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
})
state["mydf"].record_add({"record": {"rank": 6, "flavor": "Birthday cake", "favorite": 0.01}})
```

## Prepare a dataframe for editing
### `record_update`: Change an existing row

**writer provides `EditableDataframe` as a helper to facilitate manipulation**. it makes it easier to write event handlers such as adding a line,
deleting it or modifying a value, etc...
`record_update()` replaces an existing row in an `EditableDataframe` with a new one. It takes a dictionary with the following structure...

```python
import pandas
import writer as wf

df = pandas.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})

wf.init_state({
'mydf': wf.EditableDataframe(df)
})
{
"record_index": index,
"record": row_to_update
}
```

### Handle events from a dataframe editor
...where `index` is an integer specifying which row should be updated and `row_to_update` is a dictionary containing the updated row data.

**The dataframe component emits events when an action is performed**. You must subscribe to events to integrate changes to the state of the application.
In the code example above, you would update the row at index 0 with the following code:

```python
import pandas
import writer as wf

df = pandas.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
wf.init_state({
'mydf': wf.EditableDataframe(df)
state["mydf"].record_update({
"record_index": 0,
"record": {"rank": 6, "flavor": "Bubble gum", "favorite": 0.08}
})

# Subscribe this event handler to the `wf-dataframe-add` event
def on_record_add(state, payload):
payload['record']['sales'] = 0 # default value inside the dataframe
state['mydf'].record_add(payload)


# Subscribe this event handler to the `wf-dataframe-update` event
def on_record_change(state, payload):
state['mydf'].record_update(payload)


# Subscribe this event handler to the `wf-dataframe-action` event
def on_record_action(state, payload):
"""
This event corresponds to a quick action in the drop-down menu to the left of the dataframe.
"""
record_index = payload['record_index']
if payload['action'] == 'remove':
state['mydf'].record_remove(payload)
if payload['action'] == 'open':
state['record'] = state['df'].record(record_index) # dict representation of record
```

### Datastructures supported by `EditableDataframe`
### `record_remove`: Delete an existing row

`EditableDataframe` can be used with a panda dataframe, a polar dataframe and list of records.
`record_remove()` removes an existing row from an `EditableDataframe`. It takes a dictionary with the following structure...

```python
import pandas
import polars
{"record_index": index}
```

import writer as wf
...where `index` is an integer specifying which row should be deleted.

panda_df = pandas.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
polars_df = polars.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
list_of_records = [{'a': 1, 'b': 4}, {'a': 2, 'b': 5}, {'a': 3, 'b': 6}]
In the code example above, you would delete the row at index 2 with the following code:

wf.init_state({
'mypandas': wf.EditableDataframe(panda_df),
'mypolars': wf.EditableDataframe(polars_df),
'mylistofrecords': wf.EditableDataframe(list_of_records)
})
```
```python
state["mydf"].record_remove({"record_index": 0})
AccordionGuy marked this conversation as resolved.
Show resolved Hide resolved
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading