How to pop() a list n times in Python? - python

I have a photo chooser function that counts the number of files in a given directory and makes a list of them. I want it to return only 5 image URLs. Here's the function:
from os import listdir
from os.path import join, isfile
def choose_photos(account):
photos = []
# photos dir
pd = join('C:\omg\photos', account)
# of photos
nop = len([name for name in listdir(location) if isfile(name)]) - 1
# list of photos
pl = list(range(0, nop))
if len(pl) > 5:
extra = len(pl) - 5
# How can I pop extra times, so I end up with a list of 5 numbers
shuffle(pl)
for p in pl:
photos.append(join('C:\omg\photos', account, str(p) + '.jpg'))
return photos

I'll go ahead and post a couple answers. The easiest way to get some of a list is using slice notation:
pl = pl[:5] # get the first five elements.
If you really want to pop from the list this works:
while len(pl) > 5:
pl.pop()
If you're after a random selection of the choices from that list, this is probably most effective:
import random
random.sample(range(10), 3)

Since this is a list, you can just get the last five elements by slicing it:
last_photos = photos[5:]
This will return a shallow copy, so any edit in any of the lists will be reflected in the other. If you don't want this behaviour you should first make a deep copy.
import copy
last_photos = copy.deepcopy(photos)[5:]
edit:
should of course have been [5:] instead of [:-5]
But if you actually want to 'pop' it 5 times, this means you want the list without its last 5 elements...

In most languages pop() removes and returns the last element from a collection. So to remove and return n elements at the same time, how about:
def getPhotos(num):
return [pl.pop() for _ in range(0,num)]

Quick and simple -
a = list("test string")
print(a[5:])#['s', 't', 'r', 'i', 'n', 'g']
a[:5] = []
print(a)#['s', 't', 'r', 'i', 'n', 'g']

Related

Python - change text in string by random from a list

I want to write a loop function that go through each letter in my list called original.
original = ['ABCD', 'DCBA', 'AAAA', 'AABB']
letters = ['A', 'B', 'C', 'D']
p = 1
for o in original: # loop through the original list
for i in range(0,len(o)): # loop through each letter in selected list
if random.randint(1,10) == p: #if this gives me the probability that is met
# I want to change the current letter on the current index to
# something else different from the letter list by random (maybe random.choice)
Im new to python please can you advice.
I dont want to use class or any other library but random please
First, the zero in
for i in range(0, len(o))
is redundant. You want to give random.choice a list of letters that include everything in letters minus the current letter. The fastest way I can think of doing this is with a set:
newletters = list(set(letters).difference(o[i])
Now you have a list that includes all the letters in "letters" except for the letter at o[i].
To assign the letter (after you get it from random.choice), turn your "original" word into a list:
o_list = list(o)
and assign it as
l = random.choice(newletters)
o_list[i] = l
new_word = "".join(o_list)
As for actually inserting that new word back into your list of originals, you would have to know the index of the old word - I would use enumerate to do this:
original = ['ABCD', 'DCBA', 'AAAA', 'AABB']
letters = ['A', 'B', 'C', 'D']
p = 1
for index, o in enumerate(original): # loop through the original list
for i in range(len(o)): # loop through each letter in selected list
if random.randint(1,10) == p:
newletters = list(set(letters).difference(o[i])
o_list = list(o)
l = random.choice(newletters)
o_list[i] = l
new_word = "".join(o_list)
original[index] = new_word
In python, you can not modify strings at all. You can get letters by index, select specific strings, but not modify them. To change the said list you can use original.pop(o) and add the said edited string in the list with original.append('AB" + random.choice(letters) + 'C' as you said. To be more clear: you use list.append(element) to add element to list and you use list.pop(element) to remove element from list. Again, you can never edit strings in python, you can only create new ones and store the edited old ones, for example, new_string = old_string[:4], this particular code will store all the characters in old_string, up to index 4 into the new string. Really hope I helped!
Assuming you want to update original
import random
original = ['ABCD', 'DCBA', 'AAAA', 'AABB']
letters = ['A', 'B', 'C', 'D']
p = 1
for i, o in enumerate(original):
new_letters = [] # updated letters for word o
for c in o:
if random.randint(1,10) == p:
t = letters[:] # copy of letters
t.remove(c) # remove letter from copy (so letters remains unchanged)
new_letters.append(random.choice(t)) # choice over remaining letters
else:
new_letters.append(c)
original[i] = ''.join(new_letters) # convert newsletters list to string
# and replace in original
print(original)

Issue with Configobj-python and list items

I am trying to read .ini file with keywords having single items or list items. When I try to print single item strings and float values, it prints as h,e,l,l,o and 2, ., 1 respectively, whereas it should have been just hello and 2.1. Also, when I try to write new single item string/float/integer, there is , at the end. I am new to python and dealing with configobj. Any help is appreciated and if this question has been answered previously, please direct me to it. Thanks!
from configobj import ConfigObj
Read
config = ConfigObj('para_file.ini')
para = config['Parameters']
print(", ".join(para['name']))
print(", ".join(para['type']))
print(", ".join(para['value']))
Write
new_names = 'hello1'
para['name'] = [x.strip(' ') for x in new_names.split(",")]
new_types = '3.1'
para['type'] = [x.strip(' ') for x in new_types.split(",")]
new_values = '4'
para['value'] = [x.strip(' ') for x in new_values.split(",")]
config.write()
My para_file.ini looks like this,
[Parameters]
name = hello1
type = 2.1
value = 2
There are two parts to your question.
Options in ConfigObj can be either a string, or a list of strings.
[Parameters]
name = hello1 # This will be a string
pets = Fluffy, Spot # This will be a list with 2 items
town = Bismark, ND # This will also be a list of 2 items!!
alt_town = "Bismark, ND" # This will be a string
opt1 = foo, # This will be a list of 1 item (note the trailing comma)
So, if you want something to appear as a list in ConfigObj, you must make sure it includes a comma. A list of one item must have a trailing comma.
In Python, strings are iterable. So, even though they are not a list, they can be iterated over. That means in an expression like
print(", ".join(para['name']))
The string para['name'] will be iterated over, producing the list ['h', 'e', 'l', 'l', 'o', '1'], which Python dutifully joins together with spaces, producing
h e l l o 1

Select first 20% of list, then next 20% of list

I have a list like this with about 141 entries:
training = [40.0,49.0,77.0,...... 3122.0]
and my goal is to select the first 20% of the list. I did it like this:
testfile_first20 = training[0:int(len(set(training))*0.2)]
testfile_second20 = training[int(len(set(training))*0.2):int(len(set(training))*0.4)]
testfile_third20 = training[int(len(set(training))*0.4):int(len(set(training))*0.6)]
testfile_fourth20 = training[int(len(set(training))*0.6):int(len(set(training))*0.8)]
testfile_fifth20 = training[int(len(set(training))*0.8):]
Is there any way to do this automatically in a loop? This is my way of selecting the Kfold.
Thank you.
You can use list comprehensions:
div_length = int(0.2*len(set(training)))
testfile_divisions = [training[i*div_length:(i+1)*div_length] for i in range(5)]
This will give you your results stacked in a list:
>>> [testfile_first20, testfile_second20, testfile_third20, testfile_fourth20, testfile_fifth20]
If len(training) does not divide equally into five parts, then you can either have five full divisions with a sixth taking the remainder as follows:
import math
div_length = math.floor(0.2*len(set(training)))
testfile_divisions = [training[i*div_length:min(len(training), (i+1)*div_length)] for i in range(6)]
or you can have four full divisions with the fifth taking the remainder as follows:
import math
div_length = math.ceil(0.2*len(set(training)))
testfile_divisions = [training[i*div_length:min(len(training), (i+1)*div_length)] for i in range(5)]
Here's a simple take with list comprehension
lst = list('abcdefghijkl')
l = len(lst)
[lst[i:i+l//5] for i in range(0, l, l//5)]
# [['a', 'b'],
# ['c', 'd'],
# ['e', 'f'],
# ['g', 'h'],
# ['i', 'j'],
# ['k', 'l']]
Edit: Actually now that I look at my answer, it's not a true 20% representation as it returns 6 sublists instead of 5. What is expected to happen when the list cannot be equally divided into 5 parts? I'll leave this up for now until further clarifications are given.
You can loop this by just storing the "size" of 20% and the current starting point in two variables. Then add one to the other:
start = 0
twenty_pct = len(training) // 5
parts = []
for k in range(5):
parts.append(training[start:start+twenty_pct])
start += twenty_pct
However, I suspect there are numpy/pandas/scipy operations that might be a better match for what you want. For example, sklearn includes a function called KFold: https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.KFold.html
Something like this, but maybe you may lose an element due to rounding.
tlen = float(len(training))
testfiles = [ training[ int(i*0.2*tlen): int((i+1)*0.2*tlen) ] for i in range(5) ]

making the loop to continue to produce for outputs

Here is the code I have:
def generate(x)
two = {}
for x in range(1,7315):
two.update({vowels[random.randint(0,4)] + alpha[random.randint(0,21)]:0})
return two
generate(x)
this only returns a single value, how could I make it return multiple values?
return a tuple with your values
def returnTwoNumbers():
return (1, 0)
print(returnTwoNumbers()[0])
print(returnTwoNumbers()[1])
#output:
#1
#0
It also looks like you're trying to get a random vowel from your list of vowels. Using random.choice is a much more readable way to get a random item from a list:
import random
vowelList = ['a', 'e', 'i', 'o', 'u']
print (random.choice(vowelList))
You can use a tuple to return multiple values from a function e.g.:
return (one, two, three)
You have wrong indentation
def generate():
two = {}
for x in range(1,7315):
two.update({vowels[random.randint(0,4)] + alpha[random.randint(0,21)]:0})
return two
twos = generate()

Sub-dictionary erroneously repeated throughout dictionary?

I'm trying to store in a dictionary the number of times a given letter occurs after another given letter. For example, dictionary['a']['d'] would give me the number of times 'd' follows 'a' in short_list.
alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
short_list = ['ford','hello','orange','apple']
# dictionary to keep track of how often a given letter occurs
tally = {}
for a in alphabet:
tally[a] = 0
# dictionary to keep track of how often a given letter occurs after a given letter
# e.g. how many times does 'd' follow 'a' -- master_dict['a']['d']
master_dict = {}
for a in alphabet:
master_dict[a] = tally
def precedingLetter(letter,word):
if word.index(letter) == 0:
return
else:
return word[word.index(letter)-1]
for a in alphabet:
for word in short_list:
for b in alphabet:
if precedingLetter(b,word) == a:
master_dict[a][b] += 1
However, the entries for all of the letters (the keys) in master_dict are all the same. I can't think of another way to properly tally each letter's occurrence after another letter. Can anyone offer some insight here?
If the sub-dicts are all supposed to be updated independently after creation, you need to shallow copy them. Easiest/fastest way is with .copy():
for a in alphabet:
master_dict[a] = tally.copy()
The other approach is to initialize the dict lazily. The easiest way to do that is with defaultdict:
from collections import defaultdict
masterdict = defaultdict(lambda: defaultdict(int))
# or
from collections import Counter, defaultdict
masterdict = defaultdict(Counter)
No need to pre-create empty tallies or populate masterdict at all, and this avoids creating dicts when the letter never occurs. If you access masterdict[a] for an a that doesn't yet exist, it creates a defaultdict(int) value for it automatically. When masterdict[a][b] is accessed and doesn't exist, the count is initialized to 0 automatically.
In Addition to the first answer it could be handy to perform your search the other way around. So instead of looking for each possible pair of letters, you could iterate just over the words.
In combination with the defaultdict this could simplify the process. As an example:
from collections import defaultdict
short_list = ['ford','hello','orange','apple']
master_dict = defaultdict(lambda: defaultdict(int))
for word in short_list:
for i in range(0,len(word)-1):
master_dict[word[i]][word[i+1]] += 1
Now master_dict contains all occured letter combinations while it returns zero for all other ones. A few examples below:
print(master_dict["f"]["o"]) # ==> 1
print(master_dict["o"]["r"]) # ==> 2
print(master_dict["a"]["a"]) # ==> 0
The problem you ask about is that the master_dict[a] = tally is only assigning the same object another name, so updating it through any of the references updates them all. You could fix that by making a copy of it each time by using master_dict[a] = tally.copy() as already pointed out in #ShadowRanger's answer.
As #ShadowRanger goes on to point out, it would also be considerably less wasteful to make your master_dict a defaultdict(lambda: defaultdict(int)) because doing so would only allocate and initialize counts for the combinations that actually encountered rather than all possible 2 letter permutations (if it was used properly).
To give you a concert idea of the savings, consider that there are only 15 unique letter pairs in your sample short_list of words, yet the exhaustive approach would still create and initialize 26 placeholders in 26 dictionaries for all 676 the possible counts.
It also occurs to me that you really don't need a two-level dictionary at all to accomplish what you want since the same thing could be done with a single dictionary which had keys comprised of tuples of pairs of characters.
Beyond that, another important improvement, as pointed out in #AdmPicard's answer, is that your approach of iterating through all possible permutations and seeing if any pairs of them are in each word via the precedingLetter() function is significantly more time consuming than it would be if you just iterated over all the successive pairs of letters that actually occurred in each one of them.
So, putting all this advice together would result in something like the following:
from collections import defaultdict
from string import ascii_lowercase
alphabet = set(ascii_lowercase)
short_list = ['ford','hello','orange','apple']
# dictionary to keep track of how often a letter pair occurred after one other.
# e.g. how many times 'd' followed an 'a' -> master_dict[('a','d')]
master_dict = defaultdict(int)
try:
from itertools import izip
except ImportError: # Python 3
izip = zip
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = iter(iterable), iter(iterable) # 2 independent iterators
next(b, None) # advance the 2nd one
return izip(a, b)
for word in short_list:
for (ch1,ch2) in pairwise(word.lower()):
if ch1 in alphabet and ch2 in alphabet:
master_dict[(ch1,ch2)] += 1
# display results
unique_pairs = 0
for (ch1,ch2) in sorted(master_dict):
print('({},{}): {}'.format(ch1, ch2, master_dict[(ch1,ch2)]))
unique_pairs += 1
print('A total of {} different letter pairs occurred in'.format(unique_pairs))
print('the words: {}'.format(', '.join(repr(word) for word in short_list)))
Which produces this output from the short_list:
(a,n): 1
(a,p): 1
(e,l): 1
(f,o): 1
(g,e): 1
(h,e): 1
(l,e): 1
(l,l): 1
(l,o): 1
(n,g): 1
(o,r): 2
(p,l): 1
(p,p): 1
(r,a): 1
(r,d): 1
A total of 15 different letter pairs occurred in
the words: 'ford', 'hello', 'orange', 'apple'

Categories

Resources