<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Tutorial
Step1:  2. Load table
Graphistry works seamlessly with dataframes like Pandas and GPU RAPIDS cuDF
Step2:  3. Plot
A. Simple graphs
Build up a set of bindings. Simple graphs are
Step3:  B. Hypergraphs -- Plot arbitrary tables
The hypergraph transform is a convenient method to transform tables into graphs
Step4:  Approach 2
Step5:  3. Advanced plotting
You can then drive visual styles based on node and edge attributes
This demo starts by computing a node table. By default, you do not need to explictly provide a table of nodes, but then you may lack data for node properties
Step6:  Advanced bindings work with hypergraphs too
Hypergraphs precompute a lot of values on nodes and edges, which we can use to drive clearer visualizations
<ASSISTANT_TASK:>
Python Code:
import graphistry
# To specify Graphistry account & server, use:
# graphistry.register(api=3, username='...', password='...', protocol='https', server='hub.graphistry.com')
# For more options, see https://github.com/graphistry/pygraphistry#configure
Explanation: Tutorial: Data Analysis in Graphistry
Register
Load table
Plot: 
Simple: input is a list of edges
Arbitrary: input is a table (hypergraph transform)
Advanced plotting
Further reading
PyGraphistry
PyGraphistry demos: database connectors, ...
graph-app-kit: Streamlit graph dashboarding
UI Guide
CSV upload notebook app
1. Register
End of explanation
import pandas as pd
df = pd.read_csv('./data/honeypot.csv')
df.sample(3)
Explanation: 2. Load table
Graphistry works seamlessly with dataframes like Pandas and GPU RAPIDS cuDF
End of explanation
g = graphistry.edges(df, 'attackerIP', 'victimIP')
g.plot()
Explanation: 3. Plot
A. Simple graphs
Build up a set of bindings. Simple graphs are:
required: edge table, with src+dst ID columns, and optional additional property columns
optional: node table, with matching node ID column
See UI Guide for in-tool activity
Demo graph schema:
Input table: Above alerts df with columns | attackerIP | victimIP |
Edges: Link df's columns attackerIP -&gt; victimIP
Nodes: Unspecified; Graphistry defaults to generating based on the edges
Node colors: Graphistry defaults to inferring the commmunity
Node sizes: Graphistry defaults to the number of edges ("degree")
End of explanation
hg1 = graphistry.hypergraph(
    df,
    # Optional: Subset of columns to turn into nodes; defaults to all
    entity_types=['attackerIP', 'victimIP', 'victimPort', 'vulnName'],
    # Optional: merge nodes when their IDs appear in multiple columns
    # ... so replace nodes attackerIP::1.1.1.1 and victimIP::1.1.1.1
    # ... with just one node ip::1.1.1.1
    opts={
        'CATEGORIES': {
            'ip': ['attackerIP', 'victimIP']
        }
    })
hg1_g = hg1['graph']
hg1_g.plot()
Explanation: B. Hypergraphs -- Plot arbitrary tables
The hypergraph transform is a convenient method to transform tables into graphs:
It extracts entities from the table and links them together
Entities get linked together when they are from the same row
Approach 1: Treat each row as a node, and link it to each cell value in it
Demo graph schema:
* Edges: row -> attackerIP, row -> victimIP, row -> victimPort, row ->  volnName
* Nodes: row, attackerIP, victimIP, victimPort, vulnName
* Node colors: Automatic based on inferred commmunity
* node sizes: Number of edges
End of explanation
hg2 = graphistry.hypergraph(
    df,
    entity_types=['attackerIP', 'victimIP', 'victimPort', 'vulnName'],
    direct=True,
    opts={
        # Optional: Without, creates edges that are all-to-all for each row 
        'EDGES': {
            'attackerIP': ['victimIP', 'victimPort', 'vulnName'],
            'victimPort': ['victimIP'],
            'vulnName': ['victimIP']
        },
        # Optional: merge nodes when their IDs appear in multiple columns
        # ... so replace nodes attackerIP::1.1.1.1 and victimIP::1.1.1.1
        # ... with just one node ip::1.1.1.1
        'CATEGORIES': {
            'ip': ['attackerIP', 'victimIP']
        }
    })
hg2_g = hg2['graph']
hg2_g.plot()
Explanation: Approach 2: Link values from column entries
For more advanced hypergraph control, we can skip the row node, and control which edges are generated, by enabling direct.
Demo graph schema:
* Edges: 
  * attackerIP -> victimIP, attackerIP -> victimPort, attackerIP -> vulnName
  * victimPort -> victimIP
  * vulnName -> victimIP
* Nodes: attackerIP, victimIP, victimPort, vulnName
* Default colors: Automatic based on inferred commmunity
* Default node size: Number of edges
End of explanation
# Cell:
# Compute nodes_df by combining entities in attackerIP and victimIP
# As part of this, compute attack counts for each node 
targets_df = (
    df
    [['victimIP']]
    .drop_duplicates()
    .rename(columns={'victimIP': 'node_id'})
    .assign(type='victim')
)
attackers_df = (
    df
    .groupby(['attackerIP'])
    .agg(attacks=pd.NamedAgg(column="attackerIP", aggfunc="count"))
    .reset_index()
    .rename(columns={'attackerIP': 'node_id'}).assign(type='attacker')
)
nodes_df = pd.concat([targets_df, attackers_df])
nodes_df.sort_values(by='attacks', ascending=False)[:5]
# Cell:
# Add
# New encodings features requires api=3: `graphistry.register(api=3, username='...', password='...')
g2 = (g
      .nodes(nodes_df, 'node_id')
      # 'red', '#f00', '#ff0000'
      .encode_point_color('type', categorical_mapping={
          'attacker': 'red',
          'victim': 'white'
      }, default_mapping='gray')
      # Icons: https://fontawesome.com/v4.7/cheatsheet/
      .encode_point_icon('type', categorical_mapping={
          'attacker': 'bomb',
          'victim': 'laptop'
      })
      # Gradient
      .encode_edge_color('time(min)', palette=['blue', 'purple', 'red'], as_continuous=True)
      .encode_point_size('attacks')
      .addStyle(bg={'color': '#eee'}, page={'title': 'My Graph'})
      # Options: https://hub.graphistry.com/docs/api/1/rest/url/
      .settings(url_params={'play': 1000, 'pointSize': 0.5})
)
g2.plot(as_files=False)
Explanation: 3. Advanced plotting
You can then drive visual styles based on node and edge attributes
This demo starts by computing a node table. By default, you do not need to explictly provide a table of nodes, but then you may lack data for node properties:
Regular inferred graph nodes will only have id and degree
Hypergraph edges and row nodes will have many properties, but hypergraph entity nodes will only have id, type/category, and degree
Demo schema:
Node table: | node_id | type | attacks |
Point size: number of attacks
Point icon & color: attacker vs victim
Edge color: based on first attack
End of explanation
hg2_g._nodes.sample(3)
hg2_g._edges.sample(3)
(hg2_g
 .encode_point_color('type', categorical_mapping={
     'attackerIP': 'yellow',
     'victimIP': 'blue'
 }, default_mapping='gray')
 .encode_point_icon('type', categorical_mapping={
      'attackerIP': 'bomb',
      'victimIP': 'laptop'
 }, default_mapping='')
 .encode_edge_color('time(min)', palette=['blue', 'purple', 'red'], as_continuous=True)
 .settings(url_params={'pointsOfInterestMax': 10})
).plot()
Explanation: Advanced bindings work with hypergraphs too
Hypergraphs precompute a lot of values on nodes and edges, which we can use to drive clearer visualizations
End of explanation
<END_TASK>