import numpy as np
[docs]
def create_missingness(tensor, drop):
"""
Creates missingness for a full tensor. Slgihtly faster than entry_drop()
"""
idxs = np.argwhere(np.isfinite(tensor))
dropidxs = idxs[np.random.choice(idxs.shape[0], drop, replace=False)]
dropidxs = tuple(dropidxs.T)
tensor[dropidxs] = np.nan
[docs]
def entry_drop(tensor, drop, seed=None):
"""
Drops random values within a tensor. Finds a bare minimum cube before dropping values to ensure PCA remains viable.
Parameters
----------
tensor : ndarray
Takes a tensor of any shape. Preference for at least two values present per chord.
drop : int
To set a percentage, multiply np.sum(np.isfinite(tensor)) by the percentage
to find the relevant drop value, rounding to nearest int.
Returns
-------
None : tensor is modified with missing values.
"""
# Track chords for each mode to ensure bare minimum cube covers each chord at least once
if seed != None:
np.random.seed(seed)
midxs = np.zeros((tensor.ndim, max(tensor.shape)))
for i in range(tensor.ndim):
midxs[i] = [1 for n in range(tensor.shape[i])] + [
0 for m in range(len(midxs[i]) - tensor.shape[i])
]
modecounter = np.arange(tensor.ndim)
# Remove bare minimum cube idxs from droppable values
idxs = np.argwhere(np.isfinite(tensor))
while np.sum(midxs) > 0:
removable = False
ran = np.random.choice(idxs.shape[0], 1)
ranidx = idxs[ran][0]
for counter, i in enumerate(ranidx):
if midxs[modecounter[counter], i] > 0:
removable = True
midxs[modecounter[counter], i] = 0
if removable == True:
idxs = np.delete(idxs, ran, axis=0)
assert idxs.shape[0] >= drop
# Drop values
dropidxs = idxs[np.random.choice(idxs.shape[0], drop, replace=False)]
dropidxs = [tuple(dropidxs[i]) for i in range(drop)]
for i in dropidxs:
tensor[i] = np.nan
def joint_entry_drop(big_tensor, small_tensor, drop, seed=None):
if seed != None:
np.random.seed(seed)
# Track chords for each mode to ensure bare minimum cube covers each chord at least once
midxs1 = np.zeros((big_tensor.ndim, max(big_tensor.shape)))
for i in range(big_tensor.ndim):
midxs1[i] = [1 for n in range(big_tensor.shape[i])] + [
0 for m in range(len(midxs1[i]) - big_tensor.shape[i])
]
modecounter1 = np.arange(big_tensor.ndim)
midxs2 = np.zeros((small_tensor.ndim, max(small_tensor.shape)))
for i in range(small_tensor.ndim):
midxs2[i] = [1 for n in range(small_tensor.shape[i])] + [
0 for m in range(len(midxs2[i]) - small_tensor.shape[i])
]
modecounter2 = np.arange(small_tensor.ndim)
# Remove bare minimum cube idxs from droppable values
idxs1 = np.argwhere(np.isfinite(big_tensor))
while np.sum(midxs1) > 0:
removable = False
ran = np.random.choice(idxs1.shape[0], 1)
ranidx = idxs1[ran][0]
for counter, i in enumerate(ranidx):
if midxs1[modecounter1[counter], i] > 0:
removable = True
midxs1[modecounter1[counter], i] = 0
if removable == True:
idxs1 = np.delete(idxs1, ran, axis=0)
idxs2 = np.argwhere(np.isfinite(small_tensor))
while np.sum(midxs2) > 0:
removable = False
ran = np.random.choice(idxs2.shape[0], 1)
ranidx = idxs2[ran][0]
for counter, i in enumerate(ranidx):
if midxs2[modecounter2[counter], i] > 0:
removable = True
midxs2[modecounter2[counter], i] = 0
if removable == True:
idxs2 = np.delete(idxs2, ran, axis=0)
# Combine droppable idxs
temp1 = np.hstack((np.full((idxs1.shape[0], 1), 1), idxs1))
temp2 = np.hstack((np.full((idxs2.shape[0], 1), 2), idxs2))
diff = idxs1.shape[1] - idxs2.shape[1]
for _ in range(diff):
temp2 = np.hstack((temp2, np.full((temp2.shape[0], 1), 0)))
joint_idxs = np.vstack((temp1, temp2))
assert joint_idxs.shape[0] >= drop
# Select dropped idxs
dropidxs = joint_idxs[np.random.choice(joint_idxs.shape[0], drop, replace=False)]
# Separate dropped idxs and drop in corresponding tensors
tensor1_dropped, tensor2_dropped = 0, 0
for i in range(dropidxs.shape[0]):
if dropidxs[i, 0] == 1:
tensor1_dropped += 1
if dropidxs[i, 0] == 2:
tensor2_dropped += 1
if tensor1_dropped > 0:
dropidxs1 = np.zeros((tensor1_dropped, dropidxs.shape[1]), dtype=int)
counter = 0
for i in range(dropidxs.shape[0]):
if dropidxs[i, 0] == 1:
dropidxs1[counter] = dropidxs[i]
counter += 1
dropidxs1 = np.delete(dropidxs1, 0, 1)
dropidxs1 = [tuple(dropidxs1[i]) for i in range(tensor1_dropped)]
for i in dropidxs1:
big_tensor[i] = np.nan
if tensor2_dropped > 0:
dropidxs2 = np.zeros((tensor2_dropped, dropidxs.shape[1]), dtype=int)
counter = 0
for i in range(dropidxs.shape[0]):
if dropidxs[i, 0] == 2:
dropidxs2[counter] = dropidxs[i]
counter += 1
dropidxs2 = np.delete(dropidxs2, 0, 1)
for i in range(diff):
dropidxs2 = np.delete(dropidxs2, -1, 1)
dropidxs2 = [tuple(dropidxs2[i]) for i in range(tensor2_dropped)]
for i in dropidxs2:
small_tensor[i] = np.nan
[docs]
def chord_drop(tensor, drop, seed=None):
"""
Removes chords along axis = 0 of a tensor.
Parameters
----------
tensor : ndarray
Takes a tensor of any shape.
drop : int
To set a percentage, multiply tensor.shape[0] by the percentage
to find the relevant drop value, rounding to nearest int.
Returns
-------
None : tensor is modified with missing chords.
"""
if seed != None:
np.random.seed(seed)
# Drop chords based on random idxs
chordlen = tensor.shape[0]
for _ in range(drop):
idxs = np.argwhere(np.isfinite(tensor))
chordidx = np.delete(idxs[np.random.choice(idxs.shape[0], 1)][0], 0, -1)
dropidxs = []
for i in range(chordlen):
dropidxs.append(tuple(np.insert(chordidx, 0, i).T))
for i in range(chordlen):
tensor[dropidxs[i]] = np.nan