Yes you can using functools.partial:
from functools import partial
def fun(x, i):
return x * i
lst1 = [partial(fun, i=i) for i in range(5)]
lst2 = [j(2) for j in lst1]
Answer from AKS on Stack Overflowpython - implementing multiple string replace() using lambda function - Stack Overflow
python - how to use str.replace in a lambda function in Pandas? - Stack Overflow
python - Lambda function for replacing characters in the string - Stack Overflow
python - Lambda Replace - Stack Overflow
Yes you can using functools.partial:
from functools import partial
def fun(x, i):
return x * i
lst1 = [partial(fun, i=i) for i in range(5)]
lst2 = [j(2) for j in lst1]
yes - lambda is actually "make function"; you will need to give it a name
lst1 = [lambda x:x*i for i in range(5)]
def replace_lambda(x):
return x * x
lst2 = [replace_lambda for i in range(5)]
print lst1
print lst2
for idx, func in enumerate(lst1):
print func(idx)
for idx, func in enumerate(lst2):
print func(idx)
result:
[<function <lambda>>, <function <lambda>>, <function <lambda>>, <function <lambda>>, <function <lambda>>]
[<function replace_lambda>, <function replace_lambda>, <function replace_lambda>, <function replace_lambda>, <function replace_lambda>]
0
1
4
9
16
0
1
4
9
16
If you really want to you can force a lambda function into it:
print ''.join(map(lambda x: bad_chars.get(x, x), stri))
But really there's absolutely no need to use a lambda function here. All you need is:
print ''.join(bad_chars.get(x, x) for x in stri)
This solution is also linear time (ie O(n)) whereas all the other solutions are potentially quadratic as they involve scanning the entire string to replace each value O(n*m) where m is the size of the bad_chars dict.
Example:
bad_chars= {"\newline":" ","\n": " ", "\b":" ", "\f":" ", "\r":" ", "\t":" ", "\v":" ", "\0x00":" "}
stri = "a \b string\n with \t lots of \v bad chars"
print ''.join(bad_chars.get(x, x) for x in stri)
Ouptut:
a string with lots of bad chars
You can't really do it that easily and if you could a lambda function is still not designed for your use case.
Multiple replacements like that are done using a regular for loop statement, and a lambda is limited to a single expression. If you have to use a function, use a normal function – it's entirely equivalent to a lambda function except that it's not limited to a single expression.
If you really must know how to do it in a single expression, you have three choices:
1) If you use unicode strings (or Python 3), and limit your bad substrings to single characters (i.e. remove "\newline"), you can use the unicode.translate method.
bad_chars = {u"\n": u" ", u"\b": u" ", u"\f": u" ", u"\r": u" ", u"\t": u" ", u"\v": u" ", u"\x00": u" "}
bad_chars_table = dict((ord(k), v) for k, v in bad_chars.iteritems())
translator = lambda s: s.translate(bad_chars_table)
print translator(u"here\nwe\tgo")
2) Use regular expressions:
translator = lambda s: re.sub(r'[\n\b\f\r\t\v\x00]', ' ', s)
3) You can use reduce which can be used to reduce a sequence using a binary operation, essentially repeatedly calling a function of two arguments with the current value and an element of the sequence to get the next value.
translator = lambda s: reduce(lambda x, (from, to): x.replace(from, to), bad_chars.iteritems(), s)
As you can see, the last solution is much more difficult to understand than:
def translator(s):
for original, replacement in bad_chars.iteritems():
s = s.replace(original, replacement)
return s
And both solutions do the same thing. It's often better to program for the end, not for the means. For an arbitrary problem a comprehensible single-expression solution wouldn't exist at all.
This is the same as :
def replaceUsingMapAndLambda(sent, a1, a2):
# We create a lambda that only works if we input a1 or a2 and swaps them.
newSent = []
for x in sent:
if x != a1 and x != a2:
newSent.append(x)
elif x == a2:
newSent.append(a1)
else:
newSent.append(a2)
print(newSent)
return ''.join(newSent)
Lambda is a keyword to create an anonymous function and map applies this anonymous function to every element of the list and returns result.
To avoid any confusion, lets first extract the lambda function and see what it does. The lambda function is defined as
lambda x: x if(x != a1 and x != a2) else a1 if x == a2 else a2
A lambda statement avoids the definition of creating a named function, however any lambda function can still be defined as a normal function. This specific lambda function uses a ternary operator. This can be expanded to a regular if-else statement. This would lead to an equivalent regular function like this
def func(x, a1, a2):
if x != a1 and x != a2:
return x
elif x == a2:
return a1
else:
return a2
The first occurence of lambda could be replaced with a generator:
return sum(x.fee for x in self.bookings.values())
Similar for the second:
return sum(x.donation for x in self.bookings.values())
The third:
'\n'.join('{0} {1}'.format(x.runner_id, x.club_id) for x in self.bookings.values())
Since sum and string.join can deal with a generator, you can use the generator expression directly as an argument to these functions.
Use a generator:
return sum(x.fee for x in self.bookings.values())
Generally, generators and comprehensions improve readability, especially if you have to use a lambda to make map or filter work. Moreover, map and lambda don't play well together performance-wise (Python List Comprehension Vs. Map).
You can use str.translate for replacing multiple characters at once. str.maketrans helps you create the required mapping:
eSToAvoid = 'éêèÉÊÈ'
textFile.translate(str.maketrans(eSToAvoid, 'e' * len(eSToAvoid)))
While the str.replace can only replace one substring with another, re.sub can replace a pattern.
In [55]: eSToAvoid = 'éêèÉÊÈ'
In [58]: import re
test cases:
In [61]: re.sub(r'[éêèÉÊÈ]', 'e', 'foobar')
Out[61]: 'foobar'
In [62]: re.sub(r'[éêèÉÊÈ]', 'e', eSToAvoid)
Out[62]: 'eeeeee'
In [63]: re.sub(r'[éêèÉÊÈ]', 'e', 'testingè,É foobar è É')
Out[63]: 'testinge,e foobar e e'
The string replace approach is:
In [70]: astr = 'testingè,É foobar è É'
...: for e in eSToAvoid:
...: astr = astr.replace(e,'e')
...:
In [71]: astr
Out[71]: 'testinge,e foobar e e'
the replace is applied sequentially to astr. This can't be expressed as a list comprehension (or map). A list comprehensions most naturally replaces a loop that collects its results in a list (with list.append).
There's nothing wrong with the for loop. It's actually faster:
In [72]: %%timeit
...: astr = 'testingè,É foobar è É'
...: for e in eSToAvoid:
...: astr = astr.replace(e,'e')
...:
...:
1.37 µs ± 8.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [73]: timeit re.sub(r'[éêèÉÊÈ]', 'e', 'testingè,É foobar è É')
2.79 µs ± 15.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [77]: timeit astr.translate(str.maketrans(eSToAvoid, 'e' * len(eSToAvoid)))
2.56 µs ± 14.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
reduce
In [93]: from functools import reduce
In [96]: reduce(lambda s,e: s.replace(e,'e'),eSToAvoid, 'testingè,É foobar è É' )
Out[96]: 'testinge,e foobar e e'
In [97]: timeit reduce(lambda s,e: s.replace(e,'e'),eSToAvoid, 'testingè,É foobar è É' )
2.11 µs ± 32.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
For fun you could also explore some of the idea presented here:
Cleanest way to combine reduce and map in Python
You want to 'accumulate' changes, and to do that, you need some sort of accumulator, something that hangs on to the last replace. itertools has an accumulate function, and Py 3.8 introduced a := walrus operator.
generator
In [110]: def foo(astr, es):
...: for e in es:
...: astr = astr.replace(e,'e')
...: yield astr
...:
In [111]: list(foo(astr, eSToAvoid))
Out[111]:
['testingè,É foobar è É',
'testingè,É foobar è É',
'testinge,É foobar e É',
'testinge,e foobar e e',
'testinge,e foobar e e',
'testinge,e foobar e e']
Or [s for s in foo(astr, eSToAvoid)] in place of the list(). This highlights that fact that a list comprehension returns a list of strings, even if the strings accumulate the changes.
Well, technically, yes:
print_limit = lambda lim: (print(1), [print(c + 1) for c in range(2, lim- 3)])
But the real question is why? That code I gave does the same thing as yours, but it looks hideous. It's a bunch of mangled gibberish that actually happens to do something (though I really don't understand what).
Lambdas aren't a full replacement for functions. They are meant for when you need to pass a function to something, but the function is very simple, usually something like:
def myfunc(args):
return expression
It's a fairly common pattern with things like sorting:
mylist = [(0, 2), (4, 3), (5, 1)]
mylist.sort(key=lambda v: v[1])
mylist
# [(5, 1), (0, 2), (4, 3)]
In fact, they feel so strongly about not using lambdas too broadly that the official style guide for Python, PEP 8, says you shouldn't even give a name to one.
If you intend to reuse it, give it a real function definition.
I don't think you can put while-loops inside lambdas, and if you could, you shouldn't. You can get around this with map or similar, but be aware of the Zen of Python.
Lambda expressions are usually short and easy to read. Your print_limit function is more readable as it is.
An easier way to go would be to use pandas str methods, namely findall (to find all digits using the regex \d+) and join (to join the resulting list of digit substrings together):
>>> df.tel_no.str.findall("\d+").str.join("")
0 18607528792
1 19497228838
Name: tel_no, dtype: object
I agree that using regex matching is a good solution to your problem, but I can at least address the problem with your code.
You current code is:
df['tel_no'].apply(lambda x: x.replace(i, '') for i in ['+','-','tel:'])
Python parses this (perhaps surprisingly) as:
df['tel_no'].apply(
(
(lambda x: x.replace(i, ''))
for i in ['+','-','tel:'])
)
)
That is, you have written a generator comprehension, creating a new anonymous function at each iteration of the loop. You have not created a single anonymous function with a generator comprehension inside it!
Obviously, generators are not callable, which is what caused the error.
Your attempt reflects two additional misunderstandings:
Comprehension syntax cannot be used outside of an actual comprehension. Perhaps you meant to write
lambda x: (x.replace(i, '')) for i in ['+','-','tel:']), which would at least be one function that contains a generator comprehension.String functions like
str.replacedo not modify the string. They return a new string. See the example below.
s1 = 'hello'
s2 = s1.replace('e', 'f')
# s1 will be unchanged
assert s1 == 'hello'
# s2 will be changed
assert s2 == 'hfllo'
To write this as a function, you would need to use def, not `lambda:
def clean_tel(x):
for bad_string in ['+', '-', 'tel:']:
x = x.replace(bad_string, '')
return x
df['tel_no'].apply(clean_tel)
Or you can omit the loop and write it like this:
df['tel_no'].apply(
lambda x: x.replace('+', '').replace('-', '').replace('tel:', '')
)
Refer to captured groups as shown below:
print(df.replace(r'(\d+/\d+/) (\d+)', r'\1\2', regex=True))
Courses Fee Duration Discount Date
r1 Spark 20000 30day 1000 23/3/2022
r2 PySpark 25000 40days 2300 99/5/34
r3 Hadoop 26000 NaN 1500 7/7/2122
r4 NaT 22000 None 1200 6/12/2024
Pandas.Dataframe.replace() doesn't accept a function as the replacement value, so it simply converts it to a string.
To use a function replacement like in re.sub() you have to use Pandas.Series.str.replace()
df.Date = df.Date.str.replace(r'(\d+/\d+/ \d+)', lambda x: x.group(1).replace(" ", ""), regex=True)
print(df)
prints
Courses Fee Duration Discount Date
r1 Spark 20000 30day 1000 23/3/2022
r2 PySpark 25000 40days 2300 99/5/34
r3 Hadoop 26000 NaN 1500 7/7/2122
r4 NaT 22000 None 1200 6/12/2024