Python - How do i build a dictionary from a text file? - python

for the class data structures and algorithms at Tilburg University i got a question in an in class test:
build a dictionary from testfile.txt, with only unique values, where if a value appears again, it should be added to the total sum of that productclass.
the text file looked like this, it was not a .csv file:
apples,1
pears,15
oranges,777
apples,-4
oranges,222
pears,1
bananas,3
so apples will be -3 and the output would be {"apples": -3, "oranges": 999...}
in the exams i am not allowed to import any external packages besides the normal: pcinput, math, etc. i am also not allowed to use the internet.
I have no idea how to accomplish this, and this seems to be a big problem in my development of python skills, because this is a question that is not given in a 'dictionaries in python' video on youtube (would be to hard maybe), but also not given in a expert course because there this question would be to simple.
hope you guys can help!
enter code here
from collections import Counter
from sys import exit
from os.path import exists, isfile
##i did not finish it, but wat i wanted to achieve was build a list of the
strings and their belonging integers. then use the counter method to add
them together
## by splitting the string by marking the comma as the split point.
filename = input("filename voor input: ")
if not isfile(filename):
print(filename, "bestaat niet")
exit()
keys = []
values = []
with open(filename) as f:
xs = f.read().split()
for i in xs:
keys.append([i])
print(keys)
my_dict = {}
for i in range(len(xs)):
my_dict[xs[i]] = xs.count(xs[i])
print(my_dict)
word_and_integers_dict = dict(zip(keys, values))
print(word_and_integers_dict)
values2 = my_dict.split(",")
for j in values2:
print( value2 )
the output becomes is this:
[['schijndel,-3'], ['amsterdam,0'], ['tokyo,5'], ['tilburg,777'], ['zaandam,5']]
{'zaandam,5': 1, 'tilburg,777': 1, 'amsterdam,0': 1, 'tokyo,5': 1, 'schijndel,-3': 1}
{}
so i got the dictionary from it, but i did not separate the values.
the error message is this:
28 values2 = my_dict.split(",") <-- here was the error
29 for j in values2:
30 print( value2 )
AttributeError: 'dict' object has no attribute 'split'

I don't understand what your code is actually doing, I think you don't know what your variables are containing, but this is an easy problem to solve in Python. Split into a list, split each item again, and count:
>>> input = "apples,1 pears,15 oranges,777 apples,-4 oranges,222 pears,1 bananas,3"
>>> parts = input.split()
>>> parts
['apples,1', 'pears,15', 'oranges,777', 'apples,-4', 'oranges,222', 'pears,1', 'bananas,3']
Then split again. Behold the list comprehension. This is an idiomatic way to transform a list to another in python. Note that the numbers are strings, not ints yet.
>>> strings = [s.split(',') for s in strings]
>>> strings
[['apples', '1'], ['pears', '15'], ['oranges', '777'], ['apples', '-4'], ['oranges', '222'], ['pears', '1'], ['bananas', '3']]
Now you want to iterate over pairs, and sum all the same fruits. This calls for a dict:
>>> result = {}
>>> for fruit, countstr in pairs:
... if fruit not in result:
... result[fruit] = 0
... result[fruit] += int(countstr)
>>> result
{'pears': 16, 'apples': -3, 'oranges': 999, 'bananas': 3}
This pattern of adding an element if it doesn't exist comes up frequently. You should checkout defaultdict in the collections module. If you use that, you don't even need the if.

Let's walk through what you need to do to. First, check if the file exists and read the contents to a variable. Second, parse each line - you need to split the line on the comma, convert the number from a string to an integer, and then pass the values to a dictionary. In this case I would recommend using defaultdict from collections, but we can also do it with a standard dictionary.
from os.path import exists, isfile
from collections import defaultdict
filename = input("filename voor input: ")
if not isfile(filename):
print(filename, "bestaat niet")
exit()
# this reads the file to a list, removing newline characters
with open(filename) as f:
line_list = [x.strip() for x in f]
# create a dictionary
my_dict = {}
# update the value in the dictionary if it already exists,
# otherwise add it to the dictionary
for line in line_list:
k, v_str = line.split(',')
if k in my_dict:
my_dict[k] += int(v_str)
else:
my_dict[k] = int(v_str)
# print the dictionary
table_str = '{:<30}{}'
print(table_str.format('Item','Count'))
print('='*35)
for k,v in sorted(my_dict.item()):
print(table_str.format(k,v))

Related

Remove duplicates from large list but remove both if it does exist?

So I have a text file like this
123
1234
123
1234
12345
123456
You can see 123 appears twice so both instances should be removed. but 12345 appears once so it stays. My text file is about 70,000 lines.
Here is what I came up with.
file = open("test.txt",'r')
lines = file.read().splitlines() #to ignore the '\n' and turn to list structure
for appId in lines:
if(lines.count(appId) > 1): #if element count is not unique remove both elements
lines.remove(appId) #first instance removed
lines.remove(appId) #second instance removed
writeFile = open("duplicatesRemoved.txt",'a') #output the left over unique elements to file
for element in lines:
writeFile.write(element + "\n")
When I run this I feel like my logic is correct, but I know for a fact the output is suppose to be around 950, but Im still getting 23000 elements in my output so a lot is not getting removed. Any ideas where the bug could reside?
Edit: I FORGOT TO MENTION. An element can only appear twice MAX.
Use Counter from built in collections:
In [1]: from collections import Counter
In [2]: a = [123, 1234, 123, 1234, 12345, 123456]
In [3]: a = Counter(a)
In [4]: a
Out[4]: Counter({123: 2, 1234: 2, 12345: 1, 123456: 1})
In [5]: a = [k for k, v in a.items() if v == 1]
In [6]: a
Out[6]: [12345, 123456]
For your particular problem I will do it like this:
from collections import defaultdict
out = defaultdict(int)
with open('input.txt') as f:
for line in f:
out[line.strip()] += 1
with open('out.txt', 'w') as f:
for k, v in out.items():
if v == 1: #here you use logic suitable for what you want
f.write(k + '\n')
Be careful about removing elements from a list while still iterating over that list. This changes the behavior of the list iterator, and can make it skip over elements, which may be part of your problem.
Instead, I suggest creating a filtered copy of the list using a list comprehension - instead of removing elements that appear more than twice, you would keep elements that appear less than that:
file = open("test.txt",'r')
lines = file.read().splitlines()
unique_lines = [line for line in lines if lines.count(line) <= 2] # if it appears twice or less
with open("duplicatesRemoved.txt", "w") as writefile:
writefile.writelines(unique_lines)
You could also easily modify this code to look for only one occurrence (if lines.count(line) == 1) or for more than two occurrences.
You can count all of the elements and store them in a dictionary:
dic = {a:lines.count(a) for a in lines}
Then remove all duplicated one from array:
for k in dic:
if dic[k]>1:
while k in lines:
lines.remove(k)
NOTE: The while loop here is becaues line.remove(k) removes first k value from array and it must be repeated till there's no k value in the array.
If the for loop is complicated, you can use the dictionary in another way to get rid of duplicated values:
lines = [k for k, v in dic.items() if v==1]

Using a list as key-value pair to be inserted

Hello I have a list that I wish to insert into a dictionary - however not each element a new element in the dictionary - the list itself is 2 items long and should be used as "key-value" pair.
Or (as knowing python there are dozens of ways to do something so maybe this isn't even necessary). The base problem is that I wish to split a string into 2 parts around a delimiter and use the left as "key" and the right as "value":
for line in file:
if "=" in line:
tpair = line.split("=",1)
constantsMap.update(tpair)
Of course I could do a manual split like:
for line in file:
if "=" in line:
p = line.find("=")
constantsMap[line[:p]] = line[p+1:]
But that doesn't seem to be idiomaticcally "python", so I was wondering if there's a more clean way?
You can use sequence unpacking here:
key,val = line.split("=", 1)
constantsMap[key] = val
See a demonstration below:
>>> line = "a=1"
>>> constantsMap = {}
>>> key,val = line.split("=", 1)
>>> constantsMap[key] = val
>>> constantsMap
{'a': '1'}
>>>

Name of a list changing as I go through a loop

I am trying to make 100 lists with names such as: list1, list2, list3, etc. Essentially what I would like to do is below (although I know it doesn't work I am just not sure why).
num_lists=100
while i < num_lists:
intial_pressure_{}.format(i) = []
centerline_temperature_{}.format(i) = []
And then I want to loop through each list inserting data from a file but I am unsure how I can have the name of the list change in that loop. Since I know this won't work.
while i < num_lists:
initial_pressure_i[0] = value
I'm sure what I'm trying to do is really easy, but my experience with python is only a couple of days. Any help is appreciated.
Thanks
Instead of creating 100 list variables, you can create 100 lists inside of a list. Just do:
list_of_lists = [[] for _ in xrange(100)]
Then, you can access lists on your list by doing:
list_of_lists[0] = some_value # First list
list_of_lists[1] = some_other_value # Second list
# ... and so on
Welcome to Python!
Reading your comments on what you are trying to do, I suggest ditching your current approach. Select an easier data structure to work with.
Suppose you have a list of files:
files = ['data1.txt', 'data2.txt',...,'dataN.txt']
Now you can loop over those files in turn:
data = {}
for file in files:
data[file] = {}
with open(file,'r') as f:
lines=[int(line.strip()) for line in f]
data[file]['temps'] = lines[::2] #even lines just read
data[file]['pressures'] = lines[1::2] #odd lines
Then you will have a dict of dict of lists like so:
{'data1.txt': {'temps': [1, 2, 3,...], 'pressures': [1,2,3,...]},
'data2.txt': {'temps': [x,y,z,...], 'pressures': [...]},
...}
Then you can get your maxes like so:
max(data['data1.txt']['temps'])
Just so you can see what the data will look like, run this:
data = {}
for i in range(100):
item = 'file' + str(i)
data[item] = {}
kind_like_file_of_nums = [float(x) for x in range(10)]
data[item]['temps'] = kind_like_file_of_nums[0::2]
data[item]['pres'] = kind_like_file_of_nums[1::2]
print(data)
You could just make a dictionary of lists. Here's an example found in a similar thread:
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for i in a:
... for j in range(int(i), int(i) + 2):
... d[j].append(i)
...
>>> d
defaultdict(<type 'list'>, {1: ['1'], 2: ['1', '2'], 3: ['2']})
>>> d.items()
[(1, ['1']), (2, ['1', '2']), (3, ['2'])]

Reverse a dictionary

I'm trying to write a program for school. I'm a biotech major and this is a required course, but I'm not a programmer. So, this is probably easy for many, but difficult for me. Anyway, I have a text file with about 30 lines. Each line has a movie name listed first and actors who appeared in the movie, separated by commas following. Here's what I have so far:
InputName = input('What is the name of the file? ')
File = open(InputName, 'r+').readlines()
ActorLst = []
for line in File:
MovieActLst = line.split(',')
Movie = MovieActLst[0]
Actors = MovieActLst[1:]
for actor in Actors:
if actor not in ActorLst:
ActorLst.append(actor)
MovieDict = {Movie: Actors for x in MovieActLst}
print (MovieDict)
print(len(MovieDict))
Output(shortened):
What is the name of the file? Movies.txt
{"Ocean's Eleven": ['George Clooney', 'Brad Pitt', 'Elliot Gould', 'Casey Affleck', 'Carl Reiner', 'Julia Roberts', 'Angie Dickinson', 'Steve Lawrence', 'Wayne Newton\n']}
1
{'Up in the Air': ['George Clooney', 'Sam Elliott', 'Jason Bateman\n']}
1
{'Iron Man': ['Robert Downey Jr', 'Jeff Bridges', 'Gwyneth Paltrow\n']}
1
{'The Big Lebowski': ['Jeff Bridges', 'John Goodman', 'Julianne Moore', 'Sam Elliott\n']}
1
I have created a dictionary (MovieDict) that contains a movie name for the key and a list of actors for the values. There are about 30 movie names (keys). I need to figure out how to iterate through this dictionary to essentially reverse it. I want a dictionary that contains an actor as a key and the movies they play in as the values.
However, I think I have created a list of dictionaries as well instead of one dictionary and now I have really confused myself! Any suggestions?
Trivial using collections.defaultdict:
from collections import defaultdict
reverse = defaultdict(list)
for movie, actors in MovieDict.items():
for actor in actors:
reverse[actor].append(movie)
Thedefaultdict class differs from dict because when you try to access a key that does not exist, it creates it and sets its value to an item created by the factory passed to the constructor(list in the above code), this avoids catching the KeyError or checking if the key is in the dictionary.
Putting this with Steven Rumbalski's loop results in:
from collections import defaultdict
in_fname = input('What is the name of the file? ')
in_file = open(in_fname, 'r+')
movie_to_actors = {}
actors_to_movie = defaultdict(list)
for line in in_file:
#assumes python3:
movie, *actors = line.strip().split(',')
#python2 you can do actors=line.strip().split(',');movie=actors.pop(0)
movie_to_actors[movie] = list(actors)
for actor in actors:
actors_to_movie[actor].append(movie)
Some explanations about the code above.
Iterating over the lines of a file
File object are iterable, and thus support iteration.
This means you can do:
for line in open('filename'):
instead of:
for line in open('filename').readlines():
(Also in python2 the latter reads all file and then splits the content, while iterating over the file does not read all file into memory[and so you may save a lot of RAM with big files]).
Tuple unpacking
To "unpack" a sequence into different variables you can use the "tuple unpacking" syntax:
>>> a,b = (0,1)
>>> a
0
>>> b
1
The syntax was extended to allow gathering of a variable number of values into a variable.
For example:
>>> head, *tail = (1, 2, 3, 4, 5)
>>> head
1
>>> tail
[2, 3, 4, 5]
>>> first, *mid, last = (0, 1, 2, 3, 4, 5)
>>> first
0
>>> mid
[1, 2, 3, 4]
>>> last
5
You can have only one "starred expression", so this does not work:
>>> first, *mid, center, *mid2, last =(0,1,2,3,4,5)
File "<stdin>", line 1
SyntaxError: two starred expressions in assignment
So basically when you have a star on the left hand side, python puts there everything that it wasn't able to put in other variables. Notice that this mean that the variable may refer to an empty list:
>>> first, *mid, last = (0,1)
>>> first
0
>>> mid
[]
>>> last
1
Using defaultdict
The defaultdict allows you to give a default value to non existent keys.
The class accepts a callable(~function or class) as parameter and calls it to build a default value everytime that it's required:
>>> def factory():
... print("Called!")
... return None
...
>>> mydict = defaultdict(factory)
>>> mydict['test']
Called!
reverse={}
keys=MovieDict.keys()
for key in keys:
val=MovieDict[key]
for actor in val:
try:
reverse[actor]=reverse[actor].append(actor)
except KeyError:
reverse[actor]=[]
reverse[actor]=reverse[actor].append(actor)
print(reverse)#retarded python 3 format! :)
That should do it.
Programming is about abstracting things, so try to write code in a way that doesn't depend on the specific problem. For example:
def csv_to_dict(seq, separator=','):
dct = {}
for item in seq:
data = [x.strip() for x in item.split(separator)]
if len(data) > 1:
dct[data[0]] = data[1:]
return dct
def flip_dict(dct):
rev = {}
for key, vals in dct.items():
for val in vals:
if val not in rev:
rev[val] = []
rev[val].append(key)
return rev
Note how these two functions don't "know" anything about "input files", "actors", "movies" and so on, but still are able to solve your problem with two lines of code:
with open("movies.txt") as fp:
print(flip_dict(csv_to_dict(fp)))
InputName = input('What is the name of the file? ')
with open(InputName, 'r') as f:
actors_by_movie = {}
movies_by_actor = {}
for line in f:
movie, *actors = line.strip().split(',')
actors_by_movie[movie] = actors
for actor in actors:
movies_by_actor.setdefault(actor, []).append(movie)
Per your naming conventions:
from collections import defaultdict
InputName = input('What is the name of the file? ')
File = open(InputName, 'rt').readlines()
ActorLst = []
ActMovieDct = defaultdict(list)
for line in File:
MovieActLst = line.strip().split(',')
Movie = MovieActLst[0]
Actors = MovieActLst[1:]
for actor in Actors:
ActMovieDct[actor].append(Movie)
# print results
for actor, movies in ActMovieDct.items():
print(actor, movies)

Counting unique words in python

In direct, my code so far is this :
from glob import glob
pattern = "D:\\report\\shakeall\\*.txt"
filelist = glob(pattern)
def countwords(fp):
with open(fp) as fh:
return len(fh.read().split())
print "There are" ,sum(map(countwords, filelist)), "words in the files. " "From directory",pattern
I want to add a code that counts unique words from pattern(42 txt files in this path) but I don't know how. Can anybody help me?
The best way to count objects in Python is to use collections.Counter class, which was created for that purposes. It acts like a Python dict but is a bit easier in use when counting. You can just pass a list of objects and it counts them for you automatically.
>>> from collections import Counter
>>> c = Counter(['hello', 'hello', 1])
>>> print c
Counter({'hello': 2, 1: 1})
Also Counter has some useful methods like most_common, visit documentation to learn more.
One method of Counter class that can also be very useful is update method. After you've instantiated Counter by passing a list of objects, you can do the same using update method and it will continue counting without dropping old counters for objects:
>>> from collections import Counter
>>> c = Counter(['hello', 'hello', 1])
>>> print c
Counter({'hello': 2, 1: 1})
>>> c.update(['hello'])
>>> print c
Counter({'hello': 3, 1: 1})
print len(set(w.lower() for w in open('filename.dat').read().split()))
Reads the entire file into memory, splits it into words using
whitespace, converts
each word to lower case, creates a (unique) set from the lowercase words, counts them
and prints the output
If you want to get count of each unique word, then use dicts:
words = ['Hello', 'world', 'world']
count = {}
for word in words :
if word in count :
count[word] += 1
else:
count[word] = 1
And you will get dict
{'Hello': 1, 'world': 2}

Categories

Resources