Skip to main content

💟 Lovely numpy

Project description

💟 Lovely NumPy

Read full docs

More lovely stuff

Working with numbers
Community

Install

pip install lovely-numpy

or

conda install -c conda-forge lovely-numpy

How to use

How often do you find yourself debugging NumPy code? You dump your variable to the cell output, and see this:

numbers
array([[[-0.3541, -0.1975, -0.6715],
        [-0.3369, -0.1975, -0.9853],
        ...,
        [-0.4739, -0.3725, -0.689 ],
        [ 2.2489,  2.4111,  2.396 ]],

       [[-0.4054, -0.25  , -0.7238],
        [-0.4226, -0.2325, -1.0724],
        ...,
        [-0.8507, -0.6702, -1.0201],
        [ 2.1633,  2.3585,  2.3263]],

       ...,

       [[-0.8507, -0.3901, -1.1944],
        [-0.7822, -0.2325, -1.4559],
        ...,
        [-1.5014, -1.2304, -1.4733],
        [ 2.1804,  2.4111,  2.4308]],

       [[-0.8335, -0.4076, -1.2293],
        [-0.8164, -0.285 , -1.5256],
        ...,
        [-1.5528, -1.2829, -1.5256],
        [ 2.1119,  2.341 ,  2.3611]]], shape=(196, 196, 3), dtype=float32)

Was it really useful for you, as a human, to see all these numbers?

What is the shape? The size?
What are the statistics?
Are any of the values nan or inf?
Is it an image of a man holding a tench?

from lovely_numpy import lo

Lo and behold!

lo(numbers)
array[196, 196, 3] f32 n=115248 (450 KiB) x∈[-2.118 |▁▅█▇▂▁▁▂▂▁| 2.640] μ=-0.388 σ=1.073

Better, eh?

lo(numbers[1,:6,1]) # Still shows values if there are not too many.
array[6] f32 x∈[-0.408, -0.232] μ=-0.340 σ=0.075 [-0.250, -0.232, -0.338, -0.408, -0.408, -0.408]
spicy = numbers[0,:12,0].copy()

spicy[0] *= 10000
spicy[1] /= 10000
spicy[2] = float('inf')
spicy[3] = float('-inf')
spicy[4] = float('nan')

spicy = spicy.reshape((2,6))
lo(spicy) # Spicy stuff
array[2, 6] f32 n=12 x∈[-3.541e+03, -3.369e-05] μ=-393.776 σ=1.113e+03 +Inf! -Inf! NaN!
lo(np.zeros((10, 10))) # A zero array - make it obvious
array[10, 10] n=100 all_zeros
lo(spicy, verbose=True)
array[2, 6] f32 n=12 x∈[-3.541e+03, -3.369e-05] μ=-393.776 σ=1.113e+03 +Inf! -Inf! NaN!
array([[-3540.5432,    -0.    , ...,        nan,    -0.4054],
       [   -0.4226,    -0.4911, ...,    -0.5424,    -0.5082]],
      shape=(2, 6), dtype=float32)

Going .deeper

lo(numbers.transpose(2,1,0)).deeper
array[3, 196, 196] f32 n=115248 (450 KiB) x∈[-2.118 |▂▅█▇▂▁▁▂▂▁| 2.640] μ=-0.388 σ=1.073
  array[196, 196] f32 n=38416 x∈[-2.118 |▂▃▅█▂▁▁▂▃▁| 2.249] μ=-0.324 σ=1.036
  array[196, 196] f32 n=38416 x∈[-1.966 |▂▂▅█▂▁▁▁▂▁| 2.429] μ=-0.274 σ=0.973
  array[196, 196] f32 n=38416 x∈[-1.804 |▃█▃▁▁▁▁▁▂▁| 2.640] μ=-0.567 σ=1.178
# You can go deeper if you need to
lo(numbers[:3,:4]).deeper(2)
array[3, 4, 3] f32 n=36 x∈[-1.125, -0.197] μ=-0.563 σ=0.280
  array[4, 3] f32 n=12 x∈[-0.985, -0.197] μ=-0.487 σ=0.259
    array[3] f32 x∈[-0.672, -0.197] μ=-0.408 σ=0.197 [-0.354, -0.197, -0.672]
    array[3] f32 x∈[-0.985, -0.197] μ=-0.507 σ=0.343 [-0.337, -0.197, -0.985]
    array[3] f32 x∈[-0.881, -0.303] μ=-0.530 σ=0.252 [-0.405, -0.303, -0.881]
    array[3] f32 x∈[-0.776, -0.303] μ=-0.506 σ=0.199 [-0.440, -0.303, -0.776]
  array[4, 3] f32 n=12 x∈[-1.072, -0.232] μ=-0.571 σ=0.281
    array[3] f32 x∈[-0.724, -0.250] μ=-0.460 σ=0.197 [-0.405, -0.250, -0.724]
    array[3] f32 x∈[-1.072, -0.232] μ=-0.576 σ=0.360 [-0.423, -0.232, -1.072]
    array[3] f32 x∈[-0.968, -0.338] μ=-0.599 σ=0.268 [-0.491, -0.338, -0.968]
    array[3] f32 x∈[-0.968, -0.408] μ=-0.651 σ=0.235 [-0.577, -0.408, -0.968]
  array[4, 3] f32 n=12 x∈[-1.125, -0.285] μ=-0.631 σ=0.280
    array[3] f32 x∈[-0.828, -0.303] μ=-0.535 σ=0.219 [-0.474, -0.303, -0.828]
    array[3] f32 x∈[-1.125, -0.285] μ=-0.628 σ=0.360 [-0.474, -0.285, -1.125]
    array[3] f32 x∈[-1.020, -0.390] μ=-0.651 σ=0.268 [-0.542, -0.390, -1.020]
    array[3] f32 x∈[-1.003, -0.478] μ=-0.708 σ=0.219 [-0.645, -0.478, -1.003]

Now in .rgb color

The important queston - is it our man?

lo(numbers).rgb

Maaaaybe? Looks like someone normalized him.

in_stats = ( (0.485, 0.456, 0.406),     # mean
             (0.229, 0.224, 0.225) )    # std

# numbers.rgb(in_stats, cl=True) # For channel-last input format
lo(numbers).rgb(denorm=in_stats)

# Denorm is used to convert the input to [0..1], which is then mapped to RGB.

# lo(numbers).rgb(denorm="imagenet") # same as above
# lo(numbers).rgb(denorm="symmetric") # [-1 .. 1] input
# lo(numbers).rgb(denorm="minmax") # Use the min/max elements in each channel to scale the input to [0..1]

It’s indeed our hero, the Tenchman!

See the .chans

# .chans will map values betwen [-1,1] to colors.
# Make our values fit into that range to avoid clipping.
mean = np.array(in_stats[0])
std = np.array(in_stats[1])
numbers_01 = (numbers*std + mean).clip(0,1)
lo(numbers_01)
array[196, 196, 3] n=115248 (900 KiB) x∈[0. |▃▆▆█▂▁▁▁▃▂| 1.000] μ=0.361 σ=0.248
lo(numbers_01).chans

Grouping

# Make 8 images with progressively higher brightness and stack them 2x2x2.
eight_images = (np.stack([numbers]*8) + np.linspace(-2, 2, 8)[:,None,None,None])
eight_images = (eight_images
                     *np.array(in_stats[1])
                     +np.array(in_stats[0])
                ).clip(0,1).reshape(2,2,2,196,196,3)

lo(eight_images)
array[2, 2, 2, 196, 196, 3] n=921984 (7.03 MiB) x∈[0. |█▃▃▃▃▃▃▂▂▃| 1.000] μ=0.382 σ=0.319
lo(eight_images).rgb

Histogram

lo(numbers+3).plt

lo(numbers+3).plt(center="mean", max_s=1000)

lo(numbers+3).plt(center="range")

Options | Docs

from lovely_numpy import set_config, config, lovely
set_config(precision=5, sci_mode=True, color=False)
lo(np.array([1.,2,np.nan]))
array[3] μ=1.50000e+00 σ=5.00000e-01 NaN! [1.00000e+00, 2.00000e+00, nan]
set_config(precision=None, sci_mode=None, color=None) # None -> Reset to defaults
lo(np.array([1.,2,np.nan]))
array[3] μ=1.500 σ=0.500 NaN! [1.000, 2.000, nan]
# Or with config context manager.
with config(sci_mode=True):
    print(lo(np.array([1,2,3])))

print(lo(np.array([1,2,3])))
array[3] i64 x∈[1, 3] μ=2.000e+00 σ=8.165e-01 [1, 2, 3]
array[3] i64 x∈[1, 3] μ=2.000 σ=0.816 [1, 2, 3]

Without Lo

from lovely_numpy import rgb, chans, plot
lovely(numbers) # Returns `str`, that's why you see ''.
# Note:  lo(x) returns a wrapper object with a `__repr__` and other methods.
'array[196, 196, 3] f32 n=115248 (450 KiB) x∈[-2.118 |▁▅█▇▂▁▁▂▂▁| 2.640] μ=-0.388 σ=1.073'
rgb(numbers, denorm=in_stats) # OR: denorm='imagenet'

chans(numbers*0.3+0.5)

plot(numbers)

Matplotlib integration | Docs

lo(numbers).rgb(in_stats).fig # matplotlib figure

lo(numbers).plt.fig.savefig('pretty.svg') # Save it
!file pretty.svg; rm pretty.svg
pretty.svg: SVG Scalable Vector Graphics image
fig = plt.figure(figsize=(8,3))
fig.set_constrained_layout(True) # type: ignore
gs = fig.add_gridspec(2,2)
ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, 0])
ax3 = fig.add_subplot(gs[1,1:])

ax2.set_axis_off()
ax3.set_axis_off()

lo(numbers_01).plt(ax=ax1)
lo(numbers_01).rgb(ax=ax2)
lo(numbers_01).chans(ax=ax3);

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lovely_numpy-0.2.23.tar.gz (27.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lovely_numpy-0.2.23-py3-none-any.whl (28.1 kB view details)

Uploaded Python 3

File details

Details for the file lovely_numpy-0.2.23.tar.gz.

File metadata

  • Download URL: lovely_numpy-0.2.23.tar.gz
  • Upload date:
  • Size: 27.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for lovely_numpy-0.2.23.tar.gz
Algorithm Hash digest
SHA256 e4735891ab29e78b6ae8872780a7d0adf149391b3320829c6c9cc44861ffc340
MD5 520fe549bba9f96775684b0d78af6fc8
BLAKE2b-256 8a692d2652dec38bea3cbc4acfef2cf025dfdeefbd557d0d877d853787cf31af

See more details on using hashes here.

File details

Details for the file lovely_numpy-0.2.23-py3-none-any.whl.

File metadata

  • Download URL: lovely_numpy-0.2.23-py3-none-any.whl
  • Upload date:
  • Size: 28.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for lovely_numpy-0.2.23-py3-none-any.whl
Algorithm Hash digest
SHA256 909977aeac0e5c8937d149a459f5a090ebafcf2891f1ba38c6884d4c0350bda9
MD5 c72ea51cf9b9db25617578e516849654
BLAKE2b-256 becc320798a96109bfd7cc4cb8d40431faa0b90256b5123f80ffc08ca98eaf18

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page