C++ Sort vector by index - c++

I need to sort a std::vector by index. Let me explain it with an example:
Imagine I have a std::vector of 12 positions (but can be 18 for example) filled with some values (it doesn't have to be sorted):
Vector Index: 0 1 2 3 4 5 6 7 8 9 10 11
Vector Values: 3 0 2 3 2 0 1 2 2 4 5 3
I want to sort it every 3 index. This means: the first 3 [0-2] stay, then I need to have [6-8] and then the others. So it will end up like this (new index 3 has the value of previous idx 6):
Vector Index: 0 1 2 3 4 5 6 7 8 9 10 11
Vector Values: 3 0 2 1 2 2 3 2 0 4 5 3
I'm trying to make it in one line using std::sort + lambda but I can't get it. Also discovered the std::partition() function and tried to use it but the result was really bad hehe
Found also this similar question which orders by odd and even index but can't figure out how to make it in my case or even if it is possible: Sort vector by even and odd index
Thank you so much!
Note 0: No, my vector is not always sorted. It was just an example. I've changed the values
Note 1: I know it sound strange... think it like hte vecotr positions are like: yes yes yes no no no yes yes yes no no no yes yes yes... so the 'yes' positions will go in the same order but before the 'no' positions
Note 2: If there isn't a way with lambda then I thought making it with a loop and auxiliar vars but it's more ugly I think.
Note 3: Another example:
Vector Index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Vector Values: 3 0 2 3 2 0 1 2 2 4 5 3 2 3 0 0 2 1
Sorted Values: 3 0 2 1 2 2 2 3 0 3 2 0 4 5 3 0 2 1
The final Vector Values is sorted (in term of old index): 0 1 2 6 7 8 12 13 14 3 4 5 9 10 11 15 16 17
You can imagine those index in 2 colums, so I want first the Left ones and then the Right one:
0 1 2 3 4 5
6 7 8 9 10 11
12 13 14 15 16 17

You don't want std::sort, you want std::rotate.
std::vector<int> v = {20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31};
auto b = std::next(std::begin(v), 3); // skip first three elements
auto const re = std::end(v); // keep track of the actual end
auto e = std::next(b, 6); // the end of our current block
while(e < re) {
auto mid = std::next(b, 3);
std::rotate(b, mid, e);
b = e;
std::advance(e, 6);
}
// print the results
std::copy(std::begin(v), std::end(v), std::ostream_iterator<int>(std::cout, " "));
This code assumes you always do two groups of 3 for each rotation, but you could obviously work with whichever arbitrary ranges you wanted.
The output looks like what you'd want:
20 21 22 26 27 28 23 24 25 29 30 31
Update: #Blastfurnace pointed out that std::swap_ranges would work as well. The rotate call can be replaced with the following line:
std::swap_ranges(b, mid, mid); // passing mid twice on purpose

With the range-v3 library, you can write this quite conveniently, and it's very readable. Assuming your original vector is called input:
namespace rs = ranges;
namespace rv = ranges::views;
// input [3, 0, 2, 3, 2, 0, 1, 2, 2, 4, 5, 3, 2, 3, 0, 0, 2, 1]
auto by_3s = input | rv::chunk(3); // [[3, 0, 2], [3, 2, 0], [1, 2, 2], [4, 5, 3], [2, 3, 0], [0, 2, 1]]
auto result = rv::concat(by_3s | rv::stride(2), // [[3, 0, 2], [1, 2, 2], [2, 3, 0]]
by_3s | rv::drop(1) | rv::stride(2)) // [[3, 2, 0], [4, 5, 3], [0, 2, 1]]
| rv::join
| rs::to<std::vector<int>>; // [3, 0, 2, 1, 2, 2, 2, 3, 0, 3, 2, 0, 4, 5, 3, 0, 2, 1]
Here's a demo.

Related

Test for new id combinations in R

I am looking to create an indicator that checks whether the a group takes new combinations of numbers or not. I have a dataset like this one:
combinations <- data.frame(combination_id = c(1, 1, 1, 1,
2, 2, 2,
3,
4,
5, 5, 5, 5,
6, 6, 6),
number = c(20, 10, 12, 18,
20, 10, 12,
20,
40,
20, 10, 30, 18,
18, 30, 10))
What I want is the following:
dataset_2 <- data.frame(combination_id = c(1, 1, 1, 1,
2, 2, 2,
3,
4,
5, 5, 5, 5,
6, 6, 6),
number = c(20, 10, 12, 18,
20, 10, 12,
20,
40,
20, 10, 30, 18,
18, 30, 10),
new_combination = c(1, 1, 1, 1,
0,0,0,
0,
1,
1,1, 1, 1,
0, 0, 0))
Basically an indicator new_combination that is 1 if any of the possible combinations in that combination_id is new (i.e. not present in the lower values of combination_id) or if it is just one number that has not been seen, and is zero if a number is alone but has been seen before (as 20 in group 3) or if all combinations have been seen before (as in groups 2 and 6).
So the first group takes value of 1 because none of those numbers or combinations have been taken before, group 2 takes the value of 0 because all possible combinations are also in group 1, group 3 is only one number that has been seen before so takes the value of 0. Group 4 has a new number (40) so takes the value of 1. Group 5 has new combinations with the number 30 so takes the value of 1 and group 6 has no new combinations so takes the value of zero.
I hope this made it clear what I am looking for.
Any ideas? Thank you so much.
library(data.table)
setDT(combinations)
combinations[, new_combinations := ifelse(
combination_id %in% combinations[rowid(number) == 1, combination_id], 1, 0)]
# combination_id number new_combinations
# 1: 1 20 1
# 2: 1 10 1
# 3: 1 12 1
# 4: 1 18 1
# 5: 2 20 0
# 6: 2 10 0
# 7: 2 12 0
# 8: 3 20 0
# 9: 4 40 1
#10: 5 20 1
#11: 5 10 1
#12: 5 30 1
#13: 5 18 1
#14: 6 18 0
#15: 6 30 0
#16: 6 10 0
dplyr approach:
require(dplyr)
combinations %>% dplyr::mutate(new_combination = !duplicated(number)) %>%
group_by(combination_id) %>%
dplyr::mutate(new_combination = as.numeric(any(new_combination))) %>%
ungroup()
combination_id number new_combination
<dbl> <dbl> <dbl>
1 1 20 1
2 1 10 1
3 1 12 1
4 1 18 1
5 2 20 0
6 2 10 0
7 2 12 0
8 3 20 0
9 4 40 1
10 5 20 1
11 5 10 1
12 5 30 1
13 5 18 1
14 6 18 0
15 6 30 0
16 6 10 0
A base R option with ave + duplicated
transform(
combinations,
new_combination = ave(+!duplicated(number), combination_id, FUN = max)
)
gives
combination_id number new_combination
1 1 20 1
2 1 10 1
3 1 12 1
4 1 18 1
5 2 20 0
6 2 10 0
7 2 12 0
8 3 20 0
9 4 40 1
10 5 20 1
11 5 10 1
12 5 30 1
13 5 18 1
14 6 18 0
15 6 30 0
16 6 10 0

Extracting Columns From a Vector Matrix to store in an Array

Problem Explanation
Edit: Solution is added to the bottom in case anyone needs it
Let's Say I have a matrix with following data
1 3 2 1 7 9 5 8
9 1 3 8 6 9 4 1
3 2 4 0 4 5 7 4
3 5 6 4 6 5 7 4
I want to define a size_of_chunks variable that will store the number of chunks for example if I set size_of_chunks=2 then the matrix will become
Chunk1 | Chunk2 | Chunk3 | Chunk4
-------|---------|--------|-------
1 3 | 2 1 | 7 9 | 5 8
9 1 | 3 8 | 6 9 | 4 1
3 2 | 4 0 | 4 5 | 7 4
3 5 | 6 4 | 6 5 | 7 4
I want to take one of these chunks and use push_back to a std::vector<int>temp(row*col);
Chunk1 |
---------|
Start-> 1 3 |
9 1 |
3 2 |
End-> 3 5 |
so the temp will look like the following:
temp = {1, 3, 9, 1, 3, 2, 3, 5}
Later on when I did the same steps to chunk 2 the temp will look like this:
temp = {1, 3, 9, 1, 3, 2, 3, 5, 2, 1, 3, 8, 4, 0, 6, 4}
Code
int row = 4;
int col = 8;
int size_of_chunk = 2;
std::vector<int> A(row*col);
std::vector<int> temp(row*col);
// Here a Call for the Function to Fill A with Data
for(int r=0;r<row;r++){
for(int c=0;c<col;c+=size_of_chunk){
for(int i=0;i<c;i++){
temp.push_back(A[r*col+i]);
}
}
}
It's giving me values that doesn't match to end results how should I approach this problem ?
Thank you!
Solution
for(int c=0;c<col;c+=size_of_chunk){
for(int r=0;r<row;r++){
for(int i=0;i<size_of_chunk;i++){
temp.push_back(A[r*col+i+c]);
}
}
}
Don't specify a size for the vector in the constructor and use push_back, because that way you end up with a vector twice as big as it should be. Do one or the other.
In this case presumably it should be
std::vector<int> temp;
That is, start the vector at size zero and increase it's size using push_back.

(Python2) Combining pandas dataframe of mulilayer columns

I want to add values of dataframe of which format is same.
for exmaple
>>> my_dataframe1
class1 score
subject 1 2 3
student
0 1 2 5
1 2 3 9
2 8 7 2
3 3 4 7
4 6 7 7
>>> my_dataframe2
class2 score
subject 1 2 3
student
0 4 2 2
1 4 4 14
2 8 7 7
3 1 2 NaN
4 NaN 2 3
as you can see, the two dataframes have multi-layer columns that the main column is 'class score' and the sub columns is 'subject'.
what i want to do is that get summed dataframe which can be showed like this
score
subject 1 2 3
student
0 5 4 7
1 2 1 5
2 16 14 9
3 4 6 7
4 6 9 10
Actually, i could get this dataframe by
for i in my_dataframe1['class1 score'].index:
my_dataframe1['class1 score'].loc[i,:] = my_dataframe1['class1 score'].loc[i,:].add(my_dataframe2['class2 score'].loc[i,:], fill_value = 0)
but, when dimensions increases, it takes tremendous time to get result dataframe, and i do think it isn't good way to solve problem.
If you add values from the second dataframe, it will ignore the indexing
# you don't need `astype(int)`.
my_dataframe1.add(my_dataframe2.values, fill_value=0).astype(int)
class1 score
subject 1 2 3
student
0 5 4 7
1 6 7 23
2 16 14 9
3 4 6 7
4 6 9 10
Setup
my_dataframe1 = pd.DataFrame([
[1, 2, 5],
[2, 3, 9],
[8, 7, 2],
[3, 4, 7],
[6, 7, 7]
], pd.RangeIndex(5, name='student'), pd.MultiIndex.from_product([['class1 score'], [1, 2, 3]], names=[None, 'subject']))
my_dataframe2 = pd.DataFrame([
[4, 2, 2],
[4, 4, 14],
[8, 7, 7],
[1, 2, np.nan],
[np.nan, 2, 3]
], pd.RangeIndex(5, name='student'), pd.MultiIndex.from_product([['class2 score'], [1, 2, 3]], names=[None, 'subject']))
IIUC:
df_out = df['class1 score'].add(df2['class2 score'],fill_value=0).add_prefix('scores_')
df_out.columns = df_out.columns.str.split('_',expand=True)
df_out
Output:
scores
1 2 3
student
0 5.0 4 7.0
1 6.0 7 23.0
2 16.0 14 9.0
3 4.0 6 7.0
4 6.0 9 10.0
The way I would approach this is keep the data in the same dataframe. You could concatenate the two you have already:
big_df = pd.concat([my_dataframe1, my_dataframe2], axis=1)
Then sum over the larger dataframe, specifying level:
big_df.sum(axis=1, level='subject')

Avoiding IF in a simple mapping function

I'm trying to avoid an IF in the following mapping function:
X Y
1 11
2 10
3 9
4 8
5 7
6 6
7 5
8 4
9 3
10 2
11 1
12 12
It's basically Y = (12 - X), except when X = 12. In this case, Y = 12.
The Y vector is the reverse of the X vector shifted by one position. Is there a way to write this function using min and max or something like this in order to avoid a conditional?
I'm not attached to any programming language here
y = 12 - x%12;
works for all x from 1 to 12 inclusive. % is the C-style modulus operator, giving the remainder from dividing x by 12. That's zero if x is 12, and x for 1 to 11.
Ruby answer:
x = (1..12).to_a
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
y = x.map{|n| 12 - n % 12}
#=> [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 12]
This can be extended to work for any length by using n.max instead of 12.

For loops over a list

I want to apply two 'for' loops (slightly different from each other) on list. First 'for' loop will be from the minimum value to the left side and second from the minimum value to the right side. Following is the list:
a = [3,4,6,7,8,4,3,1,6,7,8,9,4]
# to get min index
b = a.index(min(a))
c=a[0:b+1]
d=a[b:len(a)]
for i in reversed(c):
print i
and
for i in d:
print i
So for example, first 'for' loop will run from the index value 8 to 1 and second 'for' loop will run from 8 to 13. I am not sure how to run loops in opposite directions starting from the minimum value. Any suggestions would be helpful.
>>> a = [3,4,6,7,8,4,3,1,6,7,8,9,4]
>>> b = a.index(min(a))
>>> b
7
A loop that runs from the index 7 to 0. (not 8 to 1):
>>> for i in range(b, -1, -1):
... print i, a[i]
...
7 1
6 3
5 4
4 8
3 7
2 6
1 4
0 3
A loop that run from 8 to 12:
>>> for i in range(b+1, len(a)):
... print i, a[i]
...
8 6
9 7
10 8
11 9
12 4
>>> a[b:None:-1]
[1, 3, 4, 8, 7, 6, 4, 3]
>>> a[b+1:]
[6, 7, 8, 9, 4]
UPDATE
Followings are more Pythonic methods of getting the index of the minimum value:
>>> min(xrange(len(a)), key=a.__getitem__)
7
>>> min(enumerate(a), key=lambda L: L[1])[0]
7
>>> import operator
>>> min(enumerate(a), key=operator.itemgetter(1))[0]
7