Dear John,
Printing inside the function is problematic. Your function itself does
NOT print the labels.
Just as a clarification:
F = factor(rep(1:2, 2))
by(data.frame(V = 1:4, F = F), F, function(x) { print(x); return(NULL); } )
# V F
# 1 1 1
# 3 3 1
# V F
# 2 2 2
# 4 4 2
# F: 1 <- this i
Another solution could also be possible - see below.
On 10/21/2023 10:38 PM, Leonard Mada wrote:
My mistake!
It does actually something else, which is incorrect. One could still
use (although the code is more difficult to read):
subset(tmp <- table(sample(1:10, 100, T)), tmp > 10)
2) Alte
My mistake!
It does actually something else, which is incorrect. One could still use
(although the code is more difficult to read):
subset(tmp <- table(sample(1:10, 100, T)), tmp > 10)
Sincerely,
Leonard
On 10/21/2023 10:26 PM, Leonard Mada wrote:
Dear List Members,
There was recently
Dear List Members,
There was recently an issue on R-devel (which I noticed only very late):
https://stat.ethz.ch/pipermail/r-devel/2023-October/082943.html
It is possible to use subset as well, almost as initially stated:
subset(table(sample(1:5, 100, T)), table > 10)
# Error in table > 10 :
#
Dear Rui,
On 10/18/2023 8:45 PM, Rui Barradas wrote:
split_chem_elements <- function(x, rm.digits = TRUE) {
regex <- "(?<=[A-Z])(?![a-z]|$)|(?<=.)(?=[A-Z])|(?<=[a-z])(?=[^a-z])"
if(rm.digits) {
stringr::str_replace_all(mol, regex, "#") |>
strsplit("#|[[:digit:]]") |>
lapply(\
there is anything like is.numeric, but to
parse each element of a vector.
Sincerely,
Leonard
On 10/18/2023 6:53 PM, Rui Barradas wrote:
Às 15:59 de 18/10/2023, Leonard Mada via R-help escreveu:
Dear List members,
What is the best way to test for numeric digits?
suppressWarnings(as.double(c
Dear List members,
What is the best way to test for numeric digits?
suppressWarnings(as.double(c("Li", "Na", "K", "2", "Rb", "Ca", "3")))
# [1] NA NA NA 2 NA NA 3
The above requires the use of the suppressWarnings function. Are there
any better ways?
I was working to extract chemical eleme
Dear Jason,
The code could look something like:
dummyData = data.frame(Tract=seq(1, 10, by=1),
Pct = c(0.05,0.03,0.01,0.12,0.21,0.04,0.07,0.09,0.06,0.03),
Totpop = c(4000,3500,4500,4100,3900,4250,5100,4700,4950,4800))
# Define the cutoffs
# - allow for duplicate entries;
by = 0.03; # by
Dear Jason,
I do not think that the solution based on aggregate offered by GPT was
correct. That quasi-solution only aggregates for every individual level.
As I understand, you want the cumulative sum. The idea was proposed by
Bert; you need only to sort first based on the cutoff (e.g. usin
es as documented in Writing R Extensions.
If your goal is to not use source files this way then the solution is to not
use roxygen at all. Just create those files yourself by directly editing them
from scratch.
On September 3, 2023 7:06:09 PM PDT, Leonard Mada via R-help
wrote:
Thank you
specified by the
> "Writing ..." manual.
>
> Again, further questions and elaboration should go to the
> R-package-devel list, although I think the manual is really the
> authoritative resource to follow.
>
> Cheers,
> Bert
>
>
>
> On Sun,
Dear R-List Members,
I am looking for collaborators to further develop the BioShapes
almost-package. I added a brief description below.
A.) BioShapes (Almost-) Package
The aim of the BioShapes quasi-package is to facilitate the generation
of graphical objects resembling biological and chemic
Dear R-Users,
Just out of curiosity:
Which of the 2 methods is the better one?
The results seem to differ slightly.
fun = function(u){((26104.50*u^0.03399381)/((1-u)^0.107)) - 28353.7}
uniroot(fun, c(0,1))
# 0.6048184
curve(fun(x), 0, 1)
abline(v=0.3952365, col="red")
abline(v=0.6048184,
rong:
>>
>> > x <- 2^(-20) ## considerably less then 1e-4 !!
>> > y <- 1 - x^2/2;
>> > 1/(1 - y) - 2/x^2
>> [1] 0
>>
>> It's all about the accuracy of the binary approximation of
>> floating point num
t numbers (and their arithmetic)
>
> Cheers,
> Bert
>
>
> On Fri, Aug 18, 2023 at 3:25 PM Leonard Mada via R-help
> wrote:
>
> I have added some clarifications below.
>
> On 8/18/2023 10:20 PM, Leonard Mada wrote:
> > [...]
> > After mo
I have added some clarifications below.
On 8/18/2023 10:20 PM, Leonard Mada wrote:
[...]
After more careful thinking, I believe that it is a limitation due to
floating points:
[...]
The problem really stems from the representation of 1 - x^2/2 as shown
below:
x = 1E-4
print(1 - x^2/2, digit
Dear Martin,
Thank you very much for your analysis.
I add only a small comment:
- the purpose of the modified formula was to apply l'Hospital;
- there are other ways to transform the formula; although applying
l'Hospital once is probably more robust than simple transformations (but
the computa
ve that there is room for improvement.
Sincerely,
Leonard
On 8/16/2023 9:51 AM, Iris Simmons wrote:
> You could rewrite
>
> 1 - cos(x)
>
> as
>
> 2 * sin(x/2)^2
>
> and that might give you more precision?
>
> On Wed, Aug 16, 2023, 01:50 Leonard Mada via R-help
&
Dear R-Users,
I tried to compute the following limit:
x = 1E-3;
(-log(1 - cos(x)) - 1/(cos(x)-1)) / 2 - 1/(x^2) + log(x)
# 0.4299226
log(2)/2 + 1/12
# 0.4299069
However, the result diverges as x decreases:
x = 1E-4
(-log(1 - cos(x)) - 1/(cos(x)-1)) / 2 - 1/(x^2) + log(x)
# 0.9543207
# correct: 0
t; [1] "a" "bc" "," "def" "," "adef" "," "x" ";"
> [10] "," "," "gh"
>
> I certainly would *not* claim that it is in any way superior to
>
as
text using standard overkill tools:
read.table(text="a bc,def, adef ,,gh", sep=",")
V1 V2 V3 V4 V5
1 a bc def adef NA gh
The above is a vector of texts. But if you simply want to reassemble your
initial string cleaned up a bit, you can use paste to put back com
hree!"
>
> The bug report includes the comment
> It may be possible that strsplit is not using the startoffset argument
> to pcre_exec
>
>pcre/pcre/doc/html/pcreapi.html
> A non-zero starting offset is useful when searching for another match
> in the same s
Dear R-Users,
I tried the following 3 Regex expressions in R 4.3:
strsplit("a bc,def, adef ,,gh", " |(?=,)|(?<=,)(?![ ])", perl=T)
# "a" "bc" "," "def" "," "" "adef" "," "," "gh"
strsplit("a bc,def, adef ,,gh", " |(?- the first one could also return "", "," (but probably not;
Dear Emily,
I have written a more robust version of the function:
extract.nonLetters = function(x, rm.space = TRUE, normalize=TRUE,
sort=TRUE) {
if(normalize) str = stringi::stri_trans_nfc(str);
ch = strsplit(str, "", fixed = TRUE);
ch = unique(unlist(ch));
if(sort) ch = sort(ch
Dear Emily,
Using a look-behind solves the split problem in this case. (Note: Using
Regex is in most/many cases the simplest solution.)
str = c("leucocyten + gramnegatieve staven +++ grampositieve staven ++",
"leucocyten – grampositieve coccen +")
tokens = strsplit(str, "(?<=[-+])\\s++", perl
(.S3methods(print))
> [1] 206
>
> There may be better ways, but this is what came to my mind.
> -- Bert
>
> On Wed, Mar 8, 2023 at 11:09 AM Leonard Mada via R-help
> wrote:
>
> Dear R-Users,
>
> I want to change the args() function to return by default
quot;.default")
name = fn
}
.Internal(args(name))
}
r/
Gregg
--- Original Message ---
On Wednesday, March 8th, 2023 at 12:09 PM, Leonard Mada via R-help
wrote:
Dear R-Users,
I want to change the args() function to return by default the arguments
of the default generic function:
arg
Dear R-Users,
I want to change the args() function to return by default the arguments
of the default generic function:
args = function(name, default = TRUE) {
# TODO: && is.function.generic();
if(default) {
fn = match.call()[[2]];
fn = paste0(as.character(fn), ".default"
Dear R-Users,
I noticed that *read* is not a generic function. Although it could
benefit from the functionality available for generic functions:
read = function(file, ...) UseMethod("read")
methods(read)
# [1] read.csv read.csv2 read.dcf read.delim read.delim2
read.DIF read.
Dear R-Users,
Did anyone follow more closely the SARS Cov-2 lineages?
I have done a quick check of Cov-2 mutations on the list downloaded from
NCBI (see GitHub page below); but it seems that the list contains the
cumulative mutations only for B.1 => B.1.1, but not after the B.1.1 branch:
# B.
Dear Ivan,
Thank you very much.
Indeed, I missed the download button. The csv file seems to contain all
the mutations in a usable format.
Sincerely,
Leonard
On 1/24/2023 11:29 PM, Ivan Krylov wrote:
On Tue, 24 Jan 2023 22:26:34 +0200
Leonard Mada via R-help wrote:
The data on the
Dear R-Users,
1.) Is there a package which gives the full code of a Covid-19
lineage/variant?
E.g. Omicron = B.1.1.529, while BA correspond to specific subtypes of
Omicron:
BA.x:
BA.1 = B.1.1.529.1;
BA.1.1 = B.1.1.529.1.1;
BA.1.1.5 = B.1.1.529.1.1.5;
Is there any package to offer such trans
Dear Troels,
There might be an error in one of the eqs:
# [modified] TODO: check;
mg2atp <- 10^(-7)*Mg*mgatp;
This version works:
x0 = c(
atp = 0.008,
adp = 0.1,
pi = 0.003,
pcr = 0.042,
cr = 0.004,
lactate = 0.005
) / 30;
# solved the positive value
x0[1] = 1E
Dear Troels,
I send you an updated version of the response. I think that a hybrid
approach is probably the best solution:
- Input / Starting values = free: ATP, ADP, Crea, CreaP, lactate,
inorganic phosphate;
- Output: diff(Total, given total value);
- I assume that the pH is given;
- I did NOT
Dear Akshay,
The best response was given by Andrew. "{...}" is not a closure.
This is unusual for someone used to C-type languages. But I will try to
explain some of the rationale.
In the case that "{...}" was a closure, then external variables would
need to be explicitly declared before the
Dear List-Members,
I encounter a problem while trying to integrate the following function:
integrate(function(x) x^3 / sin(x), -pi/2, pi/2)
# Error in integrate(function(x) x^3/sin(x), -pi/2, pi/2) :
# non-finite function value
# the value should be finite:
curve(x^3 / sin(x), -pi/2, pi/2)
int
Dear Terry,
The following approach may be more suitable:
fits <- lapply(argument, function)
fits.df = do.call(rbind, fits);
It works if all the lists returned by "function" have the same number of
elements.
Example:
fits.df = lapply(seq(3), function(id) {
list(
beta = rnorm(1)
Dear R-Users,
Hidden Problems with Clustering Algorithms
I stumbled recently upon a presentation about hierarchical clustering.
Unfortunately, it contains a hidden problem of clustering algorithms.
The problem is deeper and I think that it warrants a closer inspection
by the statistical commu
paragraphs, I would usually do something like:
strwrap(x = , width = 80, indent = 4)
On Fri, Oct 28, 2022 at 5:42 PM Leonard Mada via R-help
wrote:
Dear R-Users,
text = "
What is the best way to split/cut a vector of strings into lines of
preferred width?
I have come up with a simple sol
Dear R-Users,
text = "
What is the best way to split/cut a vector of strings into lines of
preferred width?
I have come up with a simple solution, albeit naive, as it involves many
arithmetic divisions.
I have an alternative idea which avoids this problem.
But I may miss some existing function
Dear R Users,
I have written some R code for multivariate polynomials in R. I am
looking forward for some help in redesigning and improving the code.
Although this code was not planned initially to be released as a
package, the functionality has become quite versatile over time. I will
provi
; Bert Gunter
>
> "The trouble with having an open mind is that people keep coming along
> and sticking things into it."
> -- Opus (aka Berkeley Breathed in his "Bloom County" comic strip )
>
>
> On Wed, May 18, 2022 at 5:08 PM Leonard Mada via R-help
> wro
else to say, have you tried it again
since?
Regards,
Andrew Simmons
On Wed, May 18, 2022 at 5:09 PM Leonard Mada via R-help
wrote:
Dear R Users,
I have run the following command in R:
# x = larger vector of strings (1200 Pubmed abstracts);
# patt = not defined;
npos = regexpr(patt,
Dear R Users,
I have run the following command in R:
# x = larger vector of strings (1200 Pubmed abstracts);
# patt = not defined;
npos = regexpr(patt, x, perl=TRUE);
# Error in regexpr(patt, x, perl = TRUE) : object 'patt' not found
The problem:
R becomes unresponsive and it takes 1-2 minut
Dear Marna,
If you want to extract the middle of those intervals, please find below
an improved variant of Luigi's code.
Note:
- it is more efficient to process the levels of a factor, instead of all
the individual strings;
- I envision that there are benefits in a large data frame (> 1 mil
Dear Jeff,
I am sending an updated version of the code.
The initial version assumed that the time points correspond to an
integer sequence. The code would fail for arbitrary times.
The new code is robust. I still assume that the data is in column-format
and that you want the time to the p
Dear Jeff,
My answer is a little bit late, but I hope it helps.
jrdf = read.table(text="Time Event_AEvent_B Lag_B
1 1 10
2 0 11
3 0 00
4 1 00
5 0 11
6 0
Dear Miluji,
something like this could help:
sapply(tapply(x$Value, x$ID, cumsum),
function(x) x[seq(4, length(x), by=4)] - c(0, x[head(seq(4,
length(x), by=4), -1)]))
1.) Step 1:
Compute the cumsum for each ID:
tapply(x$Value, x$ID, cumsum)
2.) Step 2:
- iterate over the resulting
person_names
The next method implemented is often the [ (single bracket subset) function;
this is relatively complicated to get right, but worth exploring.
I hope that gets you a little further along the road.
Martin Morgan
On 11/16/21, 11:34 PM, "R-help on behalf of Leonard Mada via R-help&q
Dear List-Members,
I want to create an S4 class with 2 data slots, as well as a plot and a
line method.
Unfortunately I lack any experience with S4 classes. I have put together
some working code - but I presume that it is not the best way to do it.
The actual code is also available on Gith
Dear List-members,
I would like to experiment with dispatching on 2 arguments and have a
few questions.
p1 = data.frame(x=1:3, coeff=1)
class(p1) = c("pm", class(p1));
I want to replace variables in a polynomial with either:
another polynomial, or another variable (character) or with a spec
uld suggest making your class through the methods
> package, with methods::setClass("pm", ...)
> See the documentation for setClass for more details, it's the
> recommended way to define classes in R.
>
> On Wed, Nov 3, 2021 at 2:36 PM Leonard Mada via R-help
>
Dear List members,
Is there a way to access the default names() function?
I tried the following:
# Multi-variable polynomial
p = data.frame(x=1:3, coeff=1)
class(p) = c("pm", class(p));
names.pm = function(p) {
# .Primitive("names")(p) # does NOT function
# .Internal("names")(p) # does NO
Dear Ravi,
I have uploaded on GitHub a version which handles also constant values
instead of functions.
Regarding named arguments: this is actually handled automatically as well:
eval.by.formula((x > 5 & x %% 2) ~ (x <= 5) ~ ., FUN, y=2, x)
# [1] 1 4 9 16 25 6 14 8 18 10
eval.by.formul
Dear Ravi,
I wrote a small replacement for ifelse() which avoids such unnecessary
evaluations (it bothered me a few times as well - so I decided to try a
small replacement).
### Example:
x = 1:10
FUN = list();
FUN[[1]] = function(x, y) x*y;
FUN[[2]] = function(x, y) x^2;
FUN[[3]] = function
Dear R Users,
I wrote a minimal parser to extract strings and comments from the
function definitions.
The string extraction works fine. But there are no comments:
a.) Are the comments stripped from the compiled packages?
b.) Alternatively: Is the deparse() not suited for this task?
b.2.)
Dear R users,
I wrote in the meantime a new function:
apply.html(html, XPATH, FUN, ...)
This function applies FUN to the nodes selected using XPATH. However, I
wonder if there is a possibility to use more simple selectors (e.g.
jQuery). Although I am not an expert with jQuery, it may be easi
Dear R Users,
I have started to compile some useful hacks for the generation of nice
descriptive statistics. I hope that these functions & hacks are useful
to the wider R community. I hope that package developers also get some
inspiration from the code or from these ideas.
I have started t
] "ababababababab"
> >
> >
> > Can I set an absolute maximum width?
> >
> > It would be nice to have an algorithm that computes a penalty
> for the
> > split and selects the split with the smallest penalty (when no
> obvious
>
eonard
On 9/29/2021 6:30 AM, Andrew Simmons wrote:
> I think what you're looking for is 'strwrap', it's in package base.
>
> On Tue, Sep 28, 2021, 22:26 Leonard Mada via R-help
> mailto:r-help@r-project.org>> wrote:
>
> Dear R-Users,
>
>
&
Dear R-Users,
Does anyone know any package or library that implements functions for
word wrapping?
I did implement a very rudimentary one (Github link below), but would
like to avoid to reinvent the wheel. Considering that word-wrapping is a
very common task, it should be available even in b
5 1.10 1.43
>> > identical(L1,L2)
>> [1] TRUE
>> > length(L1)
>> [1] 30
>> > length(dir(R.home("library"),recursive=TRUE))
>> [1] 12949
>>
>> On Sat, Sep 25, 2021 at 8:12 AM Leonard Mada via R-help
>> mailto:r-hel
dir(R.home("library"),recursive=TRUE))
> [1] 12949
>
> On Sat, Sep 25, 2021 at 8:12 AM Leonard Mada via R-help
> mailto:r-help@r-project.org>> wrote:
>
> Dear List Members,
>
>
> I tried to compute the file sizes of each installed package and the
.home("library")))
user system elapsed
0.351.101.43
identical(L1,L2)
[1] TRUE
length(L1)
[1] 30
length(dir(R.home("library"),recursive=TRUE))
[1] 12949
On Sat, Sep 25, 2021 at 8:12 AM Leonard Mada via R-help <
r-help@r-project.org> wrote:
Dear List
Dear List Members,
I tried to compute the file sizes of each installed package and the
process is terribly slow.
It took ~ 10 minutes for 512 packages / 1.6 GB total size of files.
1.) Package Sizes
system.time({
x = size.pkg(file=NULL);
})
# elapsed time: 509 s !!!
# 512 Packages
[working version]
On 9/25/2021 2:55 AM, Leonard Mada wrote:
Dear List Members,
Is there a way to extract if an installed package is from Bioconductor
or if it is a regular Cran package?
The information seems to be *not* available in:
installed.packages()
### [updated]
# Basic Info:
inf
files.
Bert Gunter
"The trouble with having an open mind is that people keep coming along
and sticking things into it."
-- Opus (aka Berkeley Breathed in his "Bloom County" comic strip )
On Fri, Sep 24, 2021 at 4:56 PM Leonard Mada via R-help
wrote:
Dear List Members,
I
n Fri, Sep 24, 2021 at 4:56 PM Leonard Mada via R-help
wrote:
Dear List Members,
Is there a way to extract if an installed package is from Bioconductor
or if it is a regular Cran package?
The information seems to be *not* available in:
installed.packages()
Sincerely,
Leonard
===
I
Dear List Members,
Is there a way to extract if an installed package is from Bioconductor
or if it is a regular Cran package?
The information seems to be *not* available in:
installed.packages()
Sincerely,
Leonard
===
I started to write some utility functions to analyse installed
Dear R users,
I have started to work on an improved version of the format.ftable
function. The code and ideas should be reused to improve other R
functions (enabling more advanced format of the character output).
However, there are a number of open questions. These are focused on
standardiz
eason not to make: include.lowest = TRUE the
>> default?
>>
>>
>> Regarding the NA:
>>
>> The user still has to suspect that some values were not
>> included and run that test.
>>
>>
>> Leonard
>>
are a lot of ways to
introduce NAs... in real projects all analysts should be suspecting this problem.
On September 17, 2021 3:01:35 PM PDT, Leonard Mada via R-help
wrote:
Thank you Andrew.
Is there any reason not to make: include.lowest = TRUE the default?
Regarding the NA:
The user st
not
>> included and run that test.
>>
>>
>> Leonard
>>
>>
>> On 9/18/2021 12:53 AM, Andrew Simmons wrote:
>>> Regarding your first point, argument 'include.lowest'
>>> already handles th
lues were not included
> and run that test.
>
>
> Leonard
>
>
> On 9/18/2021 12:53 AM, Andrew Simmons wrote:
>> Regarding your first point, argument 'include.lowest' already
>> handles this specific case, see ?.bincode
>>
>>
your own.
> Might be worth pitching to R-bugs on the wishlist.
>
>
>
> On Fri, Sep 17, 2021, 17:45 Leonard Mada via R-help
> mailto:r-help@r-project.org>> wrote:
>
> Hello List members,
>
>
> the following improvements would be useful for function cut (
Hello List members,
the following improvements would be useful for function cut (and .bincode):
1.) Argument: Include extremes
extremes = TRUE
if(right == FALSE) {
# include also right for last interval;
} else {
# include also left for first interval;
}
2.) Argument: warn = TRUE
Warn
Dear List members,
I have uploaded an improved version on Github. The function is now fully
functional:
- justify: left, right, cent...: TODO centre vs center;
- sep: separator when printing;
- pos: Top, Bottom; TODO: Middle;
see:
https://github.com/discoleo/R/blob/master/Stat/Tools.Data.
I
Dear List members,
I have uploaded an improved version on Github:
- new option: align top vs bottom;
Functions:
split.names: splits and aligns the names;
merge.align: aligns 2 string matrices;
ftable2: enhanced version of format.ftable (proof of concept);
see:
https://github.com/discoleo/R/blob
this could work. I will think about it.
>>>>
>>>>
>>>> But I was thinking more generically. Suppose we have a
>>>> series of functions:
>>>> padding(), border(), some_other_style();
>>>>
Dear List members,
I wrote some code to split long names in format.ftable. I hope it will
be useful to others as well.
Ideally, this code should be implemented natively in R. I will provide
in the 2nd part of the mail a concept how to actually implement the code
in R. This may be interesti
Hello Nevil,
you could test something like:
# the Matrix
m = matrix(1:1000, ncol=10)
m = t(m)
# Extract Data
idcol = sample(seq(100), 100, TRUE); # now columns
for(i in 1:100) {
m2 = m[ , idcol];
}
m2 = t(m2); # transpose back
It may be faster, although I did not benchmark it.
There m
ign a function right(FUN) that assigns the
>>> value to this parameter and evaluates the function FUN().
>>>
>>>
>>> There are a few ways to do this:
>>>
>>> 1.) Other parameters as ...
>>> right(FUN, value, ...) = value; and then pass &qu
t(FUN(...other parameters already specified...)) = value;
>> I wanted to explore this 2nd option: but avoid evaluating FUN,
>> unless the parameter "right" is injected into the call.
>>
>> 3.) Option 3:
>> The option you mentioned.
>&g
pendent of the method: there are still weird/unexplained
> behaviours when I try the initial code (see the latest mail with
> the improved code).
>
>
> Sincerely,
>
>
> Leonard
>
>
> On 9/13/2021 6:45 PM, Andrew Simmons wrote:
value)
> {
> which <- match.arg(which, c("bottom", "left", "top", "right"),
> several.ok = TRUE)
> # code to pad to each side here
> }
>
> Then you could use it like
>
> df <- data.frame(x=1:5, y = sample(1:5, 5))
> padd
ncan Murdoch
I want to capture the assignment event inside "right<-" and then call
the function padding() properly.
I haven't thought yet if I should use:
padding(x, right, left, ... other parameters);
or
padding(x, parameter) <- value;
It also depends if I can prope
so depends if I can properly capture the unevaluated expression
inside "right<-":
'right<-' = function(x, val) {
# x is automatically evaluated when using 'f<-'!
# but not when implementing as '%f%' = function(x, y);
}
Many thanks,
Leonard
How can I avoid evaluation?
right = function(x, val) {print("Right");};
padding = function(x) {print("Padding");};
df = data.frame(x=1:5, y = sample(1:5, 5));
### OK
'%=%' = function(x, val) {
x = substitute(x);
}
right(padding(df)) %=% 1; # but ugly
### Does NOT work
'right<-' = function(x
88 matches
Mail list logo