Skip to content

Commit 0760bb3

Browse files
DOC: Updated [ci skip] (#10)
1 parent b2bafa5 commit 0760bb3

File tree

8 files changed

+202
-27
lines changed

8 files changed

+202
-27
lines changed

cyberpandas/_accessor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33

44
def delegated_method(method, index, name, *args, **kwargs):
5-
return pd.Series(method(*args, **kwargs), index, name)
5+
return pd.Series(method(*args, **kwargs), index, name=name)
66

77

88
class Delegated:
@@ -16,7 +16,7 @@ def __get__(self, obj, type=None):
1616
index = object.__getattribute__(obj, '_index')
1717
name = object.__getattribute__(obj, '_name')
1818
result = self._get_result(obj)
19-
return pd.Series(result, index, name)
19+
return pd.Series(result, index, name=name)
2020

2121

2222
class DelegatedProperty(Delegated):

cyberpandas/ip_array.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def construct_from_string(cls, string):
5252

5353

5454
class IPArray(ExtensionArray):
55-
"""Holder for things"""
55+
"""Holder for IP Addresses."""
5656
# A note on the internal data layout. IPv6 addresses require 128 bits,
5757
# which is more than a uint64 can store. So we use a NumPy structured array
5858
# with two fields, 'hi', 'lo' to store the data. Each field is a uint64.
@@ -74,18 +74,21 @@ def __init__(self, values):
7474
# -------------------------------------------------------------------------
7575
@property
7676
def dtype(self):
77+
"""The dtype for this extension array, IPType"""
7778
return self._dtype
7879

7980
@property
8081
def shape(self):
82+
"""A length-tuple with the length of the array."""
8183
return (len(self.data),)
8284

8385
@property
8486
def nbytes(self):
85-
return 2 * 64 * len(self)
87+
"""The number of bytes taken to store this array.
8688
87-
def view(self, dtype=None):
88-
return self.data.view()
89+
It takes 16 bytes to store each addresses.
90+
"""
91+
return 16 * len(self)
8992

9093
def take(self, indexer, allow_fill=True, fill_value=None):
9194
mask = indexer == -1

cyberpandas/test_ip_pandas.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ def test_accessor_works():
143143
s.ip.is_ipv4
144144

145145

146+
def test_accessor_frame():
147+
s = pd.DataFrame({"A": ip.IPArray([0, 1, 2, 3])})
148+
s['A'].ip.is_ipv4
149+
150+
151+
146152
# ---------
147153
# Factorize
148154
# ---------

docs/source/api.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
API
2+
===
3+
4+
.. currentmodule:: cyberpandas
5+
6+
7+
.. autoclass:: IPArray

docs/source/conf.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@
3333
# ones.
3434
extensions = [
3535
'sphinx.ext.autodoc',
36+
'sphinx.ext.autosummary',
3637
'sphinx.ext.doctest',
3738
'sphinx.ext.intersphinx',
3839
'numpydoc',
40+
'IPython.sphinxext.ipython_console_highlighting',
41+
'IPython.sphinxext.ipython_directive',
3942
]
4043

4144
# Add any paths that contain templates here, relative to this directory.
@@ -160,4 +163,14 @@
160163

161164

162165
# Example configuration for intersphinx: refer to the Python standard library.
163-
intersphinx_mapping = {'https://docs.python.org/': None}
166+
intersphinx_mapping = {
167+
'python': ('https://docs.python.org/', None),
168+
'pandas': ('http://pandas-docs.github.io/pandas-docs-travis/', None),
169+
}
170+
171+
172+
ipython_execlines = [
173+
"import ipaddress",
174+
"import pandas as pd",
175+
"from cyberpandas import IPArray, to_ipaddress",
176+
]

docs/source/index.rst

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,6 @@ cyberpandas
99
cyberpandas is a library for working with arrays of IP Addresses. It's
1010
specifically designed to work well with pandas.
1111

12-
Install
13-
=======
14-
15-
With conda
16-
17-
.. code-block:: none
18-
19-
conda install -c conda-forge cyberpandas
20-
21-
Or pip
22-
23-
.. code-block:: none
24-
25-
pip install cyberpandas
26-
2712
Key Concepts
2813
============
2914

@@ -43,13 +28,24 @@ This is the container for your IPAddress data.
4328
Usage
4429
-----
4530

46-
.. code-block:: python
31+
.. ipython:: python
4732
48-
>>> from cyberpandas import IPArray
49-
>>> import pandas as pd
33+
from cyberpandas import IPArray
34+
import pandas as pd
5035
51-
>>> arr = IPArray([0, 1, 2])0000
52-
>>> arr
36+
arr = IPArray(['192.168.1.1',
37+
'2001:0db8:85a3:0000:0000:8a2e:0370:7334'])
38+
arr
39+
40+
``IPArray`` is a container for both IPv4 and IPv6 addresses. It can in turn be
41+
stored in pandas' containers:
42+
43+
.. ipython:: python
44+
45+
pd.Series(arr)
46+
pd.DataFrame({"addresses": arr})
47+
48+
See :ref:`usage` for more.
5349

5450
API
5551
===
@@ -61,6 +57,11 @@ API
6157
:maxdepth: 2
6258
:caption: Contents:
6359

60+
install.rst
61+
usage.rst
62+
api.rst
63+
64+
6465

6566

6667
Indices and tables

docs/source/install.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Install
2+
=======
3+
4+
cyberpandas requires pandas 0.23 or newer, which is currently unreleased.
5+
6+
Once pandas is installed, cyberpandas can be installed from PyPI
7+
8+
.. code-block:: none
9+
10+
pip install cyberpandas

docs/source/usage.rst

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
.. _usage:
2+
3+
Usage
4+
=====
5+
6+
This document describes how to use the methods and classes provided by
7+
``cyberpandas``.
8+
9+
We'll assume that the following imports have been performed.
10+
11+
.. ipython:: python
12+
13+
import ipaddress
14+
import pandas as pd
15+
from cyberpandas import IPArray, to_ipaddress
16+
17+
Parsing
18+
-------
19+
20+
First, you'll need some IP Address data. Much like pandas'
21+
:func:`pandas.to_datetime`, ``cyberpandas`` provides :func:`to_ipaddress` for
22+
converting sequences of anything to a specialized array, :class:`IPArray` in
23+
this case.
24+
25+
From Strings
26+
""""""""""""
27+
28+
:func:`to_ipaddress` can parse a sequence strings where each element represents
29+
an IP address.
30+
31+
.. ipython:: python
32+
33+
to_ipaddress([
34+
'192.168.1.1',
35+
'2001:0db8:85a3:0000:0000:8a2e:0370:7334',
36+
])
37+
38+
You can also parse a *container* of bytes (Python 2 parlance).
39+
40+
.. ipython:: python
41+
42+
to_ipaddress([
43+
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8\x01\x01',
44+
b' \x01\r\xb8\x85\xa3\x00\x00\x00\x00\x8a.\x03ps4',
45+
])
46+
47+
If you have a buffer / bytestring, see :ref:`from_bytes`.
48+
49+
From Integers
50+
"""""""""""""
51+
52+
IP Addresses are just integers, and :func:`to_ipaddress` can parse a sequence of
53+
them.
54+
55+
.. ipython:: python
56+
57+
to_ipaddress([
58+
3232235777,
59+
42540766452641154071740215577757643572
60+
])
61+
62+
There's also the :meth:`IPArray.from_pyints` method that does the same thing.
63+
64+
.. _from_bytes:
65+
66+
From Bytes
67+
""""""""""
68+
69+
If you have a correctly structured buffer of bytes or bytestring, you can
70+
directly construct an ``IPArray`` without any intermediate copies.
71+
72+
.. ipython:: python
73+
74+
stream = (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8\x01'
75+
b'\x01 \x01\r\xb8\x85\xa3\x00\x00\x00\x00\x8a.\x03ps4')
76+
IPArray.from_bytes(stream)
77+
78+
``stream`` is expected to be a sequence of bytes representing IP Addresses (note
79+
that it's just a bytestring that's be split across two lines for readability).
80+
Each IP Address should be 128 bits, left padded with 0s for IPv4 addresses.
81+
In particular, :meth:`IPArray.to_bytes` produces such a sequence of bytes.
82+
83+
Pandas Integration
84+
------------------
85+
86+
``IPArray`` satisfies pandas extension array interface, which means that it can
87+
safely be stored inside pandas' Series and DataFrame.
88+
89+
.. ipython:: python
90+
91+
values = to_ipaddress([
92+
0,
93+
3232235777,
94+
42540766452641154071740215577757643572
95+
])
96+
values
97+
98+
ser = pd.Series(values)
99+
ser
100+
df = pd.DataFrame({"addresses": values})
101+
df
102+
103+
Most pandas methods that make sense should work. The following section will call
104+
out points of interest.
105+
106+
Indexing
107+
""""""""
108+
109+
If your selection returns a scalar, you get back an
110+
:class:`ipaddress.IPv4Address` or :class:`ipaddress.IPv6Address`.
111+
112+
.. ipython:: python
113+
114+
ser[0]
115+
df.loc[2, 'addresses']
116+
117+
Missing Data
118+
""""""""""""
119+
120+
The address 0 (``0.0.0.0``) is used to represent missing values.
121+
122+
.. ipython:: python
123+
124+
ser.isna()
125+
ser.dropna()
126+
127+
IP Accessor
128+
-----------
129+
130+
``cyberpandas`` offers an accessor for IP-specific methods.
131+
132+
.. ipython:: python
133+
134+
ser.ip.isna
135+
df['addresses'].ip.is_ipv6

0 commit comments

Comments
 (0)