How to create dummy variable and then aggregate using scikit-learn? - python

I know it can be easily realized using the package pandas, but because it is too sparse and large (170,000 x 5000), and at the end I need to use sklearn to deal with the data again, I'm wondering if there is a way to do with sklearn. I tried the one hot encoder, but got stuck to associate dummies with the 'id'.
df = pd.DataFrame({'id': [1, 1, 2, 2, 3, 3], 'item': ['a', 'a', 'c', 'b', 'a', 'b']})
id item
0 1 a
1 1 a
2 2 c
3 2 b
4 3 a
5 3 b
dummy = pd.get_dummies(df, prefix='item', columns=['item'])
dummy.groupby('id').sum().reset_index()
id item_a item_b item_c
0 1 2 0 0
1 2 0 1 1
2 3 1 1 0
Update:
Now I'm here, and the 'id' is lost, how to do aggregation then?
lab = sklearn.preprocessing.LabelEncoder()
labels = lab.fit_transform(np.array(df.item))
enc = sklearn.preprocessing.OneHotEncoder()
dummy = enc.fit_transform(labels.reshape(-1,1))
dummy.todense()
matrix([[ 1., 0., 0.],
[ 1., 0., 0.],
[ 0., 0., 1.],
[ 0., 1., 0.],
[ 1., 0., 0.],
[ 0., 1., 0.]])

In case anyone needs a reference in the future, I put my solution here.
I used scipy sparse matrix.
First, do a grouping and count the number of records.
df = df.groupby(['id','item']).size().reset_index().rename(columns={0:'count'})
This takes some time but not days.
Then use pivot table, which I found a solution here.
from scipy.sparse import csr_matrix
def to_sparse_pivot(df, id, item, count):
id_u = list(df[id].unique())
item_u = list(np.sort(df[item].unique()))
data = df[count].tolist()
row = df[id].astype('category', categories=id_u).cat.codes
col = df[item].astype('category', categories=item_u).cat.codes
return csr_matrix((data, (row, col)), shape=(len(id_u), len(item_u)))
Then call the function
result = to_sparse_pivot(df, 'id', 'item', 'count')

OneHotEncoder requires integers, so here is one way to map your items to a unique integer. Because the mapping is one-to-one, we can also reverse this dictionary.
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
df = pd.DataFrame({'ID': [1, 1, 2, 2, 3, 3],
'Item': ['a', 'a', 'c', 'b', 'a', 'b']})
mapping = {letter: integer for integer, letter in enumerate(df.Item.unique())}
reverse_mapping = {integer: letter for letter, integer in mapping.iteritems()}
>>> mapping
{'a': 0, 'b': 2, 'c': 1}
>>> reverse_mapping
{0: 'a', 1: 'c', 2: 'b'}
Now create a OneHotEncoder and map your values.
hot = OneHotEncoder()
h = hot.fit_transform(df.Item.map(mapping).values.reshape(len(df), 1))
>>> h
<6x3 sparse matrix of type '<type 'numpy.float64'>'
with 6 stored elements in Compressed Sparse Row format>
>>> h.toarray()
array([[ 1., 0., 0.],
[ 1., 0., 0.],
[ 0., 1., 0.],
[ 0., 0., 1.],
[ 1., 0., 0.],
[ 0., 0., 1.]])
And for reference, these would be the appropriate columns:
>>> [reverse_mapping[n] for n in reverse_mapping.keys()]
['a', 'c', 'b']
From your data, you can see that the value c in the dataframe was in the third row (with an index value of 2). This has been mapped to c which you can see from the reverse mapping is the middle column. It is also the only value in the middle column of the matrix to contain a value of one, confirming the result.
Beyond this, I'm not sure where you'd be stuck. If you still have issues, please clarify.
To concatenate the ID values:
>>> np.concatenate((df.ID.values.reshape(len(df), 1), h.toarray()), axis=1)
array([[ 1., 1., 0., 0.],
[ 1., 1., 0., 0.],
[ 2., 0., 1., 0.],
[ 2., 0., 0., 1.],
[ 3., 1., 0., 0.],
[ 3., 0., 0., 1.]])
To keep the array sparse:
from scipy.sparse import hstack, lil_matrix
id_vals = lil_matrix(df.ID.values.reshape(len(df), 1))
h_dense = hstack([id_vals, h.tolil()])
>>> type(h_dense)
scipy.sparse.coo.coo_matrix
>>> h_dense.toarray()
array([[ 1., 1., 0., 0.],
[ 1., 1., 0., 0.],
[ 2., 0., 1., 0.],
[ 2., 0., 0., 1.],
[ 3., 1., 0., 0.],
[ 3., 0., 0., 1.]])

Related

python xarray - update subset of values

I need to update values in a Xarray DataArray. There are only certain values I want to update, which are stored in a dataframe together with their coordinates.
One can also reformulate the problem as both (i) update a non-continuous (non-rectangular) area of values in a DataArray and (ii) left-join dataset with new values on matching coords and rewrite old values.
Intial DataArray (Dataset):
import xarray as xr
import pandas as pd
ds = xr.Dataset(
data_vars = {"x": (("a", "b"), np.zeros((3, 4)))},
coords = {"a": np.arange(3), "b": np.arange(4)})
ds.x.values
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
New values to assign with their coordinates:
new_val = pd.DataFrame([
(0, 1, 10),
(1, 0, 11),
(2, 2, 12)],
columns=['a', 'b', 'x'])
Result I want to get:
array([[0., 10., 0., 0.],
[11., 0., 0., 0.],
[0., 0., 12., 0.]])
I was trying to use methods both in Combining data and in Assigning values with indexing turials, but no luck so far.
Result can be achieved using combine_first.
# set index and convert to xr Dataset
new_val_idx = new_val.set_index(['a', 'b'])
new_val_ds = xr.Dataset.from_dataframe(new_val_idx)
combined = new_val_ds.combine_first(ds)
combined.x.values
array([[ 0., 10., 0., 0.],
[11., 0., 0., 0.],
[ 0., 0., 12., 0.]])

How to make a row of a matrix be the column of another matrix in an elegant way with Python? [duplicate]

How could the following MATLAB code be written using NumPy?
A = zeros(5, 100);
x = ones(5,1);
A(:,1) = x;
Assigning to rows seems to work easily, but I couldn't find an example of assigning an array to a column of another array.
Use a[:,1] = x[:,0]. You need x[:,0] to select the column of x as a single numpy array. If you have the choice of how to format x, it's better to not make it a 2-dimensional array in the first place, but just a regular (row) array:
>>> a
array([[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]])
>>> x = numpy.ones(5)
>>> x
array([ 1., 1., 1., 1., 1.])
>>> a[:,1] = x
>>> a
array([[ 0., 1., 0.],
[ 0., 1., 0.],
[ 0., 1., 0.],
[ 0., 1., 0.],
[ 0., 1., 0.]])
>>> A = np.zeros((5,100))
>>> x = np.ones((5,1))
>>> A[:,:1] = x

updating specific numpy matrix columns

I have the following list of indices [2 4 3 4] which correspond to my target indices. I'm creating a matrix of zeroes with the following line of code targets = np.zeros((features.shape[0], 5)). Im wondering if its possible to slice in such a way that I could update the specific indices all at once and set those values to 1 without a for loop, ideally the matrix would look like
([0,0,1,0,0], [0,0,0,0,1], [0,0,0,1,0], [0,0,0,0,1])
I believe you can do something like this:
targets = np.zeros((4, 5))
ind = [2, 4, 3, 4]
targets[np.arange(0, 4), ind] = 1
Here is the result:
array([[ 0., 0., 1., 0., 0.],
[ 0., 0., 0., 0., 1.],
[ 0., 0., 0., 1., 0.],
[ 0., 0., 0., 0., 1.]])

Print all the non-zero element in a 2D matrix in Python

I have a sparse 2D matrix, typically something like this:
test
array([[ 1., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 2., 1., 0.],
[ 0., 0., 0., 1.]])
I'm interested in all nonzero elements in "test"
index = numpy.nonzero(test) returns a tuple of arrays giving me the indices for the nonzero elements:
index
(array([0, 2, 2, 3]), array([0, 1, 2, 3]))
For each row I would like to print out all the nonzero elements, but skipping all rows containing only zero elements.
I would appreciate hints for this.
Thanks for the hints. This solved the problem:
>>> test
array([[ 1., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 2., 1., 0.],
[ 0., 0., 0., 1.]])
>>> transp=np.transpose(np.nonzero(test))
>>> transp
array([[0, 0],
[2, 1],
[2, 2],
[3, 3]])
>>> for index in range(len(transp)):
row,col = transp[index]
print 'Row index ',row,'Col index ',col,' value : ', test[row,col]
giving me:
Row index 0 Col index 0 value : 1.0
Row index 2 Col index 1 value : 2.0
Row index 2 Col index 2 value : 1.0
Row index 3 Col index 3 value : 1.0
Given
rows, cols = np.nonzero(test)
you could also use so-called advanced integer indexing:
test[rows, cols]
For example,
test = np.array([[ 1., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 2., 1., 0.],
[ 0., 0., 0., 1.]])
rows, cols = np.nonzero(test)
print(test[rows, cols])
yields
array([ 1., 2., 1., 1.])
Use array indexing:
test[test != 0]
There is no array operation to do this per-row (instead of for the entire matrix), as that would return a variable number of elements per row. You can use something like
[row[row != 0] for row in test]
to achieve that.

Numpy: Affect diagonal elements of matrix prior to 1.10

I would like to change diagonal elements from a 2d matrix. These are both main and non-main diagonals.
numpy.diagonal()
In NumPy 1.10, it will return a read/write view, Writing to the returned
array will alter your original array.
numpy.fill_diagonal(), numpy.diag_indices()
Only works with main-diagonal elements
Here is my use case: I want to recreate a matrix of the following form, which is very trivial using diagonal notation given that I have all the x, y, z as arrays.
Try this:
>>> A = np.zeros((6,6))
>>> i,j = np.indices(A.shape)
>>> z = [1, 2, 3, 4, 5]
Now you can intuitively access any diagonal:
>>> A[i==j-1] = z
>>> A
array([[ 0., 1., 0., 0., 0., 0.],
[ 0., 0., 2., 0., 0., 0.],
[ 0., 0., 0., 3., 0., 0.],
[ 0., 0., 0., 0., 4., 0.],
[ 0., 0., 0., 0., 0., 5.],
[ 0., 0., 0., 0., 0., 0.]])
In the same way you can assign arrays to A[i==j], etc.
You could always use slicing to assign a value or array to the diagonals.
Passing in a list of row indices and a list of column indices lets you access the locations directly (and efficiently). For example:
>>> z = np.zeros((5,5))
>>> z[np.arange(5), np.arange(5)] = 1 # diagonal is 1
>>> z[np.arange(4), np.arange(4) + 1] = 2 # first upper diagonal is 2
>>> z[np.arange(4) + 1, np.arange(4)] = [11, 12, 13, 14] # first lower diagonal values
changes the array of zeros z to:
array([[ 1., 2., 0., 0., 0.],
[ 11., 1., 2., 0., 0.],
[ 0., 12., 1., 2., 0.],
[ 0., 0., 13., 1., 2.],
[ 0., 0., 0., 14., 1.]])
In general for a k x k array called z, you can set the ith upper diagonal with
z[np.arange(k-i), np.arange(k-i) + i]
and the ith lower diagonal with
z[np.arange(k-i) + i, np.arange(k-i)]
Note: if you want to avoid calling np.arange several times, you can simply write ix = np.arange(k) once and then slice that range as needed:
np.arange(k-i) == ix[:-i]
Here is another approach just for fun. You can write your own diagonal function to return of view of the diagonal you need.
import numpy as np
def diag(a, k=0):
if k > 0:
a = a[:, k:]
elif k < 0:
a = a[-k:, :]
shape = (min(a.shape),)
strides = (sum(a.strides),)
return np.lib.stride_tricks.as_strided(a, shape, strides)
a = np.arange(20).reshape((4, 5))
diag(a, 2)[:] = 88
diag(a, -2)[:] = 99
print(a)
# [[ 0 1 88 3 4]
# [ 5 6 7 88 9]
# [99 11 12 13 88]
# [15 99 17 18 19]]

Categories