0

I am trying to convert a dictionary into a form which can be plotted as a contour using matplotlib. The keys to the dictionary are a tuple of the X,Y coordinates, and the value is the reading at that coordinate. I would like put these into a three numpy array, a 1D array of x coordinates, a 1D array of y coordinates, and a 2D array of values. The respective indices of the x,y arrays should corresponds to the index of the value in the 2D array defined in the dictionary.

An edit to better define the question:

Example Input Data: Dictionary

(0,0): 1
(1.5,0): 2
(0,1.5): 3
(1.5,1.5): 4

What I would like

x = [0,1.5]
y = [0,1.5]
values = [[1,2],[3,4]]

I have got

for key in corr_data.items():
    X.append(key[0])
    Y.append(key[1])
X = list(dict.fromkeys(X))
Y = list(dict.fromkeys(Y))

which gets the x and y arrays but the values array eludes me.

Any help is appreciated

  • 3
    What have you done so far? – MaJoR Feb 08 '19 at 11:05
  • Please post some example data. If x and y are 1d and the value array is supposed to be 2d, there have to be duplicate x and y values. Especially for non-integer values this might be tricky. Do you intend to plot this with matplotlib? – Thomas Kühn Feb 08 '19 at 11:12
  • 1
    It'd be in your best interest to provide a realistic sample. If the indices aren't 0, 1, 2, etc, or not even integers, any simple answers based on the current example may not actually be applicable to your case. – Reti43 Feb 08 '19 at 12:07

3 Answers3

1

You can simply iterate over your dict and create your lists and maybe convert that lists to numpy.ndarray

x = []
y = []
vals = np.zeros(your_grid_shape)
for ((i,j), v) in your_dict.iteritems():
    x.append(i)
    y.append(j)
    vals[i, j] = v
x = list(set(x))
y = list(set(y))
Aliakbar Saleh
  • 649
  • 3
  • 10
  • Yes this was the issue I was running into, also the x and y contain mulitple values for each coordingate – Peter Scanlon Feb 08 '19 at 11:27
  • Hi, I think this is nearly there, but unfortunately, the i and j coordinate values are not positive integers (sorry that's misleading in the sample data), so the line vals[i, j] = v won't work – Peter Scanlon Feb 08 '19 at 11:46
0

I here a 'self-containing' answer in the sense that I first generate some input data, which I then convert into a dictionary and then back into the original arrays. On the way, I add some random noise to keep the x and y values close to each other but still make them unique. Following this answer, a list of all values that are 'close' to each other can be found by first rounding the values and then using np.unique.

mport numpy as np

##generating some input data:
print('input arrays')
xvals = np.linspace(1,10, 5)
print(xvals)
yvals = np.linspace(0.1, 0.4, 4)
print(yvals)
xvals, yvals = np.meshgrid(xvals, yvals)

##adding some noise to make it more interesting:
xvals += np.random.rand(*xvals.shape)*1e-3
yvals += np.random.rand(*yvals.shape)*1e-5

zvals = np.arange(xvals.size).reshape(*xvals.shape)
print(zvals)

input_dict ={
    (i,j): k for i,j,k in zip(
        list(xvals.flatten()), list(yvals.flatten()), list(zvals.flatten())
    )
}

##print(input_dict)


x,y,z = map(np.array,zip(*((x,y,z) for (x,y),z in input_dict.items())))

##this part will need some tweaking depending on the size of your
##x and y values
xlen = len(np.unique(x.round(decimals=2)))
ylen = len(np.unique(y.round(decimals=3)))


x = x.round(decimals=2).reshape(ylen,xlen)[0,:]
y = y.round(decimals=3).reshape(ylen,xlen)[:,0]
z = z.reshape(ylen,xlen)

print('\n', 'output arrays')

print(x)
print(y)
print(z)

The output looks like this:

input arrays
[ 1.    3.25  5.5   7.75 10.  ]
[0.1 0.2 0.3 0.4]
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]
 [15 16 17 18 19]]

 output arrays
[ 1.    3.25  5.5   7.75 10.  ]
[0.1 0.2 0.3 0.4]
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]
 [15 16 17 18 19]]

Old Answer:

There are a lot of assumptions in this answer, mainly because there is not quite enough information in the question. But, assuming that

  1. the x and y values are as nicely ordered as in the example data
  2. the x and y values are complete

One could go about the problem with a list comprehension and a reshaping of numpy ndarrays:

import numpy as np

input_dict = {
    (0,0): 1,
    (1,0): 2,
    (0,1): 3,
    (1,1): 4,
}

x,y,z = map(np.array,zip(*((x,y,z) for (x,y),z in input_dict.items())))

xlen = len(set(x))
ylen = len(set(y))

x = x.reshape(xlen,ylen)[0,:]
y = y.reshape(xlen,ylen)[:,0]
z = z.reshape(xlen,ylen)


print(x)
print(y)
print(z)

which gives

[0 1]
[0 1]
[[1 2]
 [3 4]]

hope this helps.

PS: If the x and y values are not in necessarily in the order suggested by the posted example data, one can still solve the issue with some clever sorting.

Thomas Kühn
  • 9,412
  • 3
  • 47
  • 63
  • Hi, the first two assumptions are correct, but the third isn't unfortunately. I'll try and think how to modify it – Peter Scanlon Feb 08 '19 at 11:49
  • @PeterScanlon I changed my solution to get rid of the third assumption (the square shape) by counting how many different x and y values there are -- please have a look. – Thomas Kühn Feb 08 '19 at 11:50
  • @PeterScanlon I just saw in your comment to the other answer that your x and y are not integers. In this case the `set(x)` may not work as expected, because of the imprecisions in floating point numbers. In this case it may be necessary to provide the amount of x and y values. Possibly one could introduce some tolerance when comparing values, but I'd have to look around a bit. – Thomas Kühn Feb 08 '19 at 11:57
  • @PeterScanlon I updated my answer to show how it can be done for non-integer x and y. – Thomas Kühn Feb 08 '19 at 12:38
0

In the REPL

In [9]: d = {(0,0): 1, (1,0): 2, (0,1): 3, (1,1): 4}
In [10]: x = set(); y = set()
In [11]: for xx, yy in d.keys():
    ...:     x.add(xx)
    ...:     y.add(yy)
In [12]: x
Out[12]: {0, 1}
In [13]: x = sorted(x) ; y = sorted(y)
In [14]: x
Out[14]: [0, 1]
In [15]: v = [[d.get((xx,yy)) for yy in y] for xx in x]
In [16]: v
Out[16]: [[1, 3], [2, 4]]

As you can see, my result is different from your example but it's common to have x corresponding to rows and y corresponding to columns. If you want a more geographic convention, swap x and y in the final list comprehension.

As a script we may write

def extract{d}:
    x = set(); y = set()
    for xx, yy in d.keys():
        x.add(xx)
        y.add(yy)
    x = sorted(x) ; y = sorted(y)
    v = [[d.get((xx,yy)) for yy in y] for xx in x]
    # = [[d.get((xx,yy)) for xx in x] for yy in y]
    return x, y, v
gboffi
  • 22,939
  • 8
  • 54
  • 85
  • 1
    Can you assume that the x and y values will behave nicely in the `set`s, even if they are floats? – Thomas Kühn Feb 08 '19 at 12:43
  • @ThomasKühn When I started to write my answer I had tuples of _integers_ as the keys... I will check and eventually change my answer, thanks for the alert, ciao. – gboffi Feb 08 '19 at 12:47
  • @ThomasKühn `set((1.2, 3.4, math.sqrt(2)))` returns `{1.2, 1.4142135623730951, 3.4}` so I guess that my answer can stand as it is... – gboffi Feb 08 '19 at 12:49
  • I rather meant the cases where your floating points *should be* equal, but are not quite because of floating point imprecisions. Se for instance [here](https://stackoverflow.com/a/4682941/2454357) what I mean. I'm honestly wondering if this can be assumed, because it would also make my first answer perfectly fine. – Thomas Kühn Feb 08 '19 at 12:57
  • @ThomasKühn Well, only the OP knows that... eg, were the floats instantiated from literals the same literal representation should always produce the same FP number, shouldn't it? — otoh, the floats could be read from a measurement device and what should be, within the measurement uncertainties, the same value has two different representations (We can think of using `Decimal` and rounding but it gets more complicated). I'll repeat myself, only the OP can tell. – gboffi Feb 08 '19 at 14:00