Python count two arrays of strings and integers - python-3.x

I have two arrays X and Y, X holds a title of a product that repeats, Y holds integer amounts of how many of X was sold.
I used Counter to count the number of occurrences for each element in X, but it does not take into account Y.
from collections import Counter
x = ['a','a','b','c','c','c','c','d','d','d','e','e']
y = [1, 5, 3, 1, 1, 1, 3, 5, 2, 1, 8, 1]
countX = Counter(x)

Use defaultdict:
from collections import defaultdict
x = ['a', 'a', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'e', 'e']
y = [1, 5, 3, 1, 1, 1, 3, 5, 2, 1, 8, 1]
output = defaultdict(int)
for prod, count in zip(x, y):
output[prod] += count
print(output)
# defaultdict(<class 'int'>, {'a': 6, 'b': 3, 'c': 6, 'd': 8, 'e': 9})

Related

My code appends the same list 10 times instead of appending 10 randomized lists?

At the moment, the while loop creates one list and appends it 10 times to the results list.
What do I need to change in the def dice() so that the while loop creates 10 different lists and appends them to the results list?
from random import choice
list = [1, 2, 3, 4, "a", "b", "c", "d"]
winner = []
ticket = [1, 2]
results = []
class Die:
def __init__(self, a_list):
self.a_list = a_list
def dice(self):
while len(results) < 10:
results.append(winner)
while len(winner) < 2:
die = choice(self.a_list)
winner.append(die)
print(results)
my_dice = Die(list)
my_dice.dice()
I wrote a different approach using zip, if this works for you:
import random
lis = [1, 2, 3, 4, "a", "b", "c", "d"]
combo_1 = []
ticket = [1, 2]
combo_2 = []
class Die:
def __init__(self, a_list):
self.a_list = a_list
def dice(self):
while len(results) < 10:
combo_2.append(random.choice(self.a_list))
combo_1.append(random.choice(self.a_list))
dice = [list(i) for i in zip(combo_1, combo_2)]
print(dice)
my_dice = Die(lis)
my_dice.dice()
Output:
[['b', 'd'], [3, 'b'], ['c', 'b'], ['d', 2], ['a', 4], ['d', 2], ['c', 'c'], ['b', 'b'], [3, 'b'], [3, 3]]
Currently you never reset winner to be empty, so it just keeps growing in size. But I think you're overcomplicating this.
You don't need a while loop if it's of fixed size - use for loop instead
If the sub-list only has two values, you don't need a loop at all
You're losing some of the benefit of having a class by having global variables
And syntactically list is a bad name for a variable because it shadows the builtin, and I would avoid having a method that prints at the end without returning anything.
How about this?
import random
class Die:
def __init__(self, dice_options):
self.dice_options = dice_options
def dice(self):
return [
[random.choice(self.dice_options), random.choice(self.dice_options)]
for _ in range(10)
]
my_dice = Die([1, 2, 3, 4, "a", "b", "c", "d"])
print(my_dice.dice())
# [[3, 'd'], ['d', 2], ['b', 4], [1, 'd'], [4, 'c'], ['b', 4], [2, 1], ['a', 'd'], ['a', 'd'], ['b', 4]]

Transform a dataframe using pivot

I am trying to transform a dataframe using pivot. Since the column contains duplicate entries, i tried to add a count column following what's suggested here (Question 10 posted in this answer).
import pandas as pd
from pprint import pprint
if __name__ == '__main__':
d = {
't': [0, 1, 2, 0, 1, 2, 0, 2, 0, 1],
'input': [2, 2, 2, 2, 2, 2, 4, 4, 4, 4],
'type': ['A', 'A', 'A', 'B', 'B', 'B', 'A', 'A', 'B', 'B'],
'value': [0.1, 0.2, 0.3, 1, 2, 3, 1, 2, 1, 1],
}
df = pd.DataFrame(d)
df = df.drop('t', axis=1)
df.insert(0, 'count', df.groupby('input').cumcount())
pd.pivot(df, index='count', columns='type', values='value')
But I still get the same error raise ValueError("Index contains duplicate entries, cannot reshape") ValueError: Index contains duplicate entries, cannot reshape.
Could someone please suggest how to resolve this error?
As far as you have more then one value associated with 'A' and 'B' you have to aggregate values somehow.
So if I've understood your issue right possible solution is the following:
#pip install pandas
import pandas as pd
d = {
't': [0, 1, 2, 0, 1, 2, 0, 2, 0, 1],
'input': [2, 2, 2, 2, 2, 2, 4, 4, 4, 4],
'type': ['A', 'A', 'A', 'B', 'B', 'B', 'A', 'A', 'B', 'B'],
'value': [0.1, 0.2, 0.3, 1, 2, 3, 1, 2, 1, 1],
}
df = pd.DataFrame(d)
df
# I've used aggfunc='sum' argument for example, the default value is 'mean'
pd.pivot_table(df, index='t', columns='type', values='value', aggfunc='sum')
Returns

Counting number of certain values in each column using pandas and collections

I have a txt file including 9 columns and 6 rows. The first 8 columns are either of these values: "1" , "2" and "3". I named these columns from "A" to "H". I named the last column: "class".
The last column is a name : "HIGH". Here is the txt file (data.txt):
1,1,1,1,2,1,1,3,HIGH
1,1,1,2,2,1,1,3,HIGH
1,1,1,1,1,1,1,3,HIGH
1,1,1,2,1,1,1,3,HIGH
1,1,1,3,2,1,1,3,HIGH
1,1,1,2,1,2,1,3,HIGH
I am trying to count the number of each value in each column and print a list that should have 3 components including the numbers of "1", "2" and "3" values in that column respectively. For example in the first column (e.g A) all values are "1". I expect to get : A : [6,0,0]. For the 8th column (e.g. H) where all values are "3", I expect to get: H : [0,0,6] or for the fourth column (e.g. D) I have two "1" , three "2" and one "3". So I expect : D : [2,3,1]. I tried to get it done using pandas and collection . Here is what I did:
import pandas as pd
from collections import Counter
df = pd.read_csv('data.txt')
df.columns = ['A','B','C','D','E','F','G','H','class']
X = df.ix[:, 0:8].values
y = df.ix[:, 8].values
deg = ['HIGH']
names = ['A','B','C','D','E','F','G','H']
for j in range(0, 8):
freqs = Counter(X[y == deg[0], j])
print(names[j],':',list(freqs.values()))
The output of the above code are empty lists. Here is what it returns:
A : []
B : []
C : []
D : []
E : []
F : []
G : []
H : []
How can I modify the above code to get what I want?
Thanks!
Use pandas.Series.value_counts
df.loc[:, :"H"].apply(pd.Series.value_counts).fillna(0).to_dict("l")
Output:
{'A': [6.0, 0.0, 0.0],
'B': [6.0, 0.0, 0.0],
'C': [6.0, 0.0, 0.0],
'D': [2, 3, 1],
'E': [3.0, 3.0, 0.0],
'F': [5.0, 1.0, 0.0],
'G': [6.0, 0.0, 0.0],
'H': [0.0, 0.0, 6.0]}
Define the following function:
def cntInts(col):
vc = col.value_counts()
return [ vc.get(i, 0) for i in range(1,4) ]
Then apply it and print results:
for k, v in df.loc[:, 'A':'H'].apply(cntInts).iteritems():
print(f'{k}: {v}')
For your data sample I got:
A: [6, 0, 0]
B: [6, 0, 0]
C: [6, 0, 0]
D: [2, 3, 1]
E: [3, 3, 0]
F: [5, 1, 0]
G: [6, 0, 0]
H: [0, 0, 6]
Or maybe it is enough to call just:
df.loc[:, 'A':'H'].apply(cntInts)
This time the result is a Series, which when printed yields:
A [6, 0, 0]
B [6, 0, 0]
C [6, 0, 0]
D [2, 3, 1]
E [3, 3, 0]
F [5, 1, 0]
G [6, 0, 0]
H [0, 0, 6]
dtype: object
Edit
Following your comments I suppose that there is something wrong with your data.
To trace the actual reason:
Define a string variable:
txt = '''1,1,1,1,2,1,1,3,HIGH
1,1,1,2,2,1,1,3,HIGH
1,1,1,1,1,1,1,3,HIGH
1,1,1,2,1,1,1,3,HIGH
1,1,1,3,2,1,1,3,HIGH
1,1,1,2,1,2,1,3,HIGH'''
Run:
import io
df = pd.read_csv(io.StringIO(txt), names=['A','B','C','D','E','F','G','H','class'])
Run my code on my data. The result should be just as expected.
Then read your input file (also into df) and run my code again.
Probably there is some difference between your data and mine.
Especially look for any extra spaces in your input file,
check also column types (after read_csv).
Solution with collections is select all columns without last, convert Counter to Series, so output is DataFrame, replace missing values by DataFrame.fillna, convert values to integers and last to dictionary by DataFrame.to_dict:
from collections import Counter
d = (df.iloc[:, :-1].apply(lambda x: pd.Series(Counter(x)))
.fillna(0)
.astype(int)
.to_dict("list"))
print (d)
{'A': [6, 0, 0], 'B': [6, 0, 0],
'C': [6, 0, 0], 'D': [1, 4, 1],
'E': [3, 3, 0], 'F': [5, 1, 0],
'G': [6, 0, 0], 'H': [0, 0, 6]}
Only pandas solution with pandas.value_counts:
d = (df.iloc[:, :-1].apply(pd.value_counts)
.fillna(0)
.astype(int)
.to_dict("list"))
print (d)
{'A': [6, 0, 0], 'B': [6, 0, 0],
'C': [6, 0, 0], 'D': [2, 3, 1],
'E': [3, 3, 0], 'F': [5, 1, 0],
'G': [6, 0, 0], 'H': [0, 0, 6]}
Working within python, since your end result is a dictionary:
from string import ascii_uppercase
from collections import Counter, defaultdict
from itertools import chain, product
import csv
d = defaultdict(list)
fieldnames = ascii_uppercase[:9]
# test.csv is your file above
with open('test.csv') as csvfile:
reader = csv.DictReader(csvfile, fieldnames = list(fieldnames))
reader = Counter(chain.from_iterable(row.items() for row in reader))
for col, value in product(fieldnames, ("1","2","3")):
if col != fieldnames[-1]:
d[col].append(reader.get((col,value), 0))
print(d)
defaultdict(list,
{'A': [6, 0, 0],
'B': [6, 0, 0],
'C': [6, 0, 0],
'D': [2, 3, 1],
'E': [3, 3, 0],
'F': [5, 1, 0],
'G': [6, 0, 0],
'H': [0, 0, 6]})

How do I order double list of elements of this type: [[1,2,3], [a,b,c]]?

I have a double list of this type: dl = [[13, 22, 41], ['c', 'b', 'a']], in which, each element dl[0][i] belongs a value in dl[1][i] (with the same index). How can I sort my list using dl[0] values as my order criteria, maintainning linked both sublists? Sublist are kind of 'linked data', so the previous dl[0][i] and dl[1][i] values must match their index after sorting the parent entire list, using as sorting criteria, the first sublist values
I expect something like:
input: dl = [ [14,22,7,17], ['K', 'M', 'F','A'] ]
output: dl = [ [7, 14, 17, 22], ['F', 'K', 'A', 'M'] ]
This was way too much fun to write. I don't doubt that this function can be greatly improved, but this is what I've gotten in a very short amount of time and should get you started.
I've included some tests just so you can verify that this does indeed do what you want.
from unittest import TestCase, main
def sort_by_first(data):
sorted_data = []
for seq in data:
zipped_to_first = zip(data[0], seq)
sorted_by_first = sorted(zipped_to_first)
unzipped_data = zip(*sorted_by_first)
sorted_data.append(list(tuple(unzipped_data)[1]))
return sorted_data
class SortByFirstTestCase(TestCase):
def test_sort(self):
output_1 = sort_by_first([[1, 3, 5, 2, 4], ['a', 'b', 'c', 'd', 'e']])
self.assertEqual(output_1, [[1, 2, 3, 4, 5], ['a', 'd', 'b', 'e', 'c']])
output_2 = sort_by_first([[9, 1, 5], [21, 22, 23], ['spam', 'foo', 'bar']])
self.assertEqual(output_2, [[1, 5, 9], [22, 23, 21], ['foo', 'bar', 'spam']])
if __name__ == '__main__':
main()
Updated for what you're looking for, selection sort but added another line to switch for the second list to match the first.
for i in range(len(dl[0])):
min_idx = i
for j in range(i+1, len(dl[0])):
if dl[0][min_idx] > dl[0][j]:
min_idx = j
dl[0][i], dl[0][min_idx] = dl[0][min_idx], dl[0][i]
dl[1][i], dl[1][min_idx] = dl[1][min_idx], dl[1][i]
You can try solving this with a for loop also:
dl = [ [3,2,1], ['c', 'b', 'a'] ]
for i in range(0,len(dl)):
dl[i].sort()
print(dl)

(Python) Finding all possible partitions of a list of lists subject to a size limit for a partition

Suppose that I have a list k = [[1,1,1],[2,2],[3],[4]], with size limit c = 4.
Then I will like to find all possible partitions of k subject ot c. Ideally, the result should be:
[ {[[1,1,1],[3]], [[2,2], [4]]}, {[[1,1,1],[4]], [[2,2], [3]]}, {[[1,1,1]], [[2,2], [3], [4]]}, ..., {[[1,1,1]], [[2,2]], [[3]], [[4]]} ]
where I used set notation { } in the above example (actual case its [ ]) to make it clearer as to what a partition is, where each partition contains groups of lists grouped together.
I implemented the following algorithm but my results do not tally:
def num_item(l):
flat_l = [item for sublist in l for item in sublist]
return len(flat_l)
def get_all_possible_partitions(lst, c):
p_opt = []
for l in lst:
p_temp = [l]
lst_copy = lst.copy()
lst_copy.remove(l)
iterations = 0
while num_item(p_temp) <= c and iterations <= len(lst_copy):
for l_ in lst_copy:
iterations += 1
if num_item(p_temp + [l_]) <= c:
p_temp += [l_]
p_opt += [p_temp]
return p_opt
Running get_all_possible_partitions(k, 4), I obtain:
[[[1, 1, 1], [3]], [[2, 2], [3], [4]], [[3], [1, 1, 1]], [[4], [1, 1, 1]]]
I understand that it does not remove duplicates and exhaust the possible combinations, which I am stuck on.
Some insight will be great! P.S. I did not manage to find similar questions :/
I think this does what you want (explanations in comments):
# Main function
def get_all_possible_partitions(lst, c):
yield from _get_all_possible_partitions_rec(lst, c, [False] * len(lst), [])
# Produces partitions recursively
def _get_all_possible_partitions_rec(lst, c, picked, partition):
# If all elements have been picked it is a complete partition
if all(picked):
yield tuple(partition)
else:
# Get all possible subsets of unpicked elements
for subset in _get_all_possible_subsets_rec(lst, c, picked, [], 0):
# Add the subset to the partition
partition.append(subset)
# Generate all partitions that complete the current one
yield from _get_all_possible_partitions_rec(lst, c, picked, partition)
# Remove the subset from the partition
partition.pop()
# Produces all possible subsets of unpicked elements
def _get_all_possible_subsets_rec(lst, c, picked, current, idx):
# If we have gone over all elements finish
if idx >= len(lst): return
# If the current element is available and fits in the subset
if not picked[idx] and len(lst[idx]) <= c:
# Mark it as picked
picked[idx] = True
# Add it to the subset
current.append(lst[idx])
# Generate the subset
yield tuple(current)
# Generate all possible subsets extending this one
yield from _get_all_possible_subsets_rec(lst, c - len(lst[idx]), picked, current, idx + 1)
# Remove current element
current.pop()
# Unmark as picked
picked[idx] = False
# Only allow skip if it is not the first available element
if len(current) > 0 or picked[idx]:
# Get all subsets resulting from skipping current element
yield from _get_all_possible_subsets_rec(lst, c, picked, current, idx + 1)
# Test
k = [[1, 1, 1], [2, 2], [3], [4]]
c = 4
partitions = list(get_all_possible_partitions(k, c))
print(*partitions, sep='\n')
Output:
(([1, 1, 1],), ([2, 2],), ([3],), ([4],))
(([1, 1, 1],), ([2, 2],), ([3], [4]))
(([1, 1, 1],), ([2, 2], [3]), ([4],))
(([1, 1, 1],), ([2, 2], [3], [4]))
(([1, 1, 1],), ([2, 2], [4]), ([3],))
(([1, 1, 1], [3]), ([2, 2],), ([4],))
(([1, 1, 1], [3]), ([2, 2], [4]))
(([1, 1, 1], [4]), ([2, 2],), ([3],))
(([1, 1, 1], [4]), ([2, 2], [3]))
If all elements in the list are unique, then you can use bit.
Assume k = [a,b,c], which length is 3, then there are 2^3 - 1 = 7 partions:
if you use bit to compresent a, b, c, there will be
001 -> [c]
010 -> [b]
011 -> [b, c]
100 -> [a]
101 -> [a,c]
110 -> [a,b]
111 -> [a,b,c]
so, the key to solving this question is obvious now.
Note: This answer is actually for a closed linked question.
If you only want to return the bipartitions of the list you can utilize more_iterools.set_partions:
>>> from more_itertools import set_partitions
>>>
>>> def get_bipartions(lst):
... half_list_len = len(lst) // 2
... if len(lst) % 2 == 0:
... return list(
... map(tuple, [
... p
... for p in set_partitions(lst, k=2)
... if half_list_len == len(p[0])
... ]))
... else:
... return list(
... map(tuple, [
... p
... for p in set_partitions(lst, k=2)
... if abs(half_list_len - len(p[0])) < 1
... ]))
...
>>> get_bipartions(['A', 'B', 'C'])
[(['A'], ['B', 'C']), (['B'], ['A', 'C'])]
>>> get_bipartions(['A', 'B', 'C', 'D'])
[(['A', 'B'], ['C', 'D']), (['B', 'C'], ['A', 'D']), (['A', 'C'], ['B', 'D'])]
>>> get_bipartions(['A', 'B', 'C', 'D', 'E'])
[(['A', 'B'], ['C', 'D', 'E']), (['B', 'C'], ['A', 'D', 'E']), (['A', 'C'], ['B', 'D', 'E']), (['C', 'D'], ['A', 'B', 'E']), (['B', 'D'], ['A', 'C', 'E']), (['A', 'D'], ['B', 'C', 'E'])]

Resources