R语言中的循环函数(Grouping Function)

本文详细介绍了R语言中apply家族的各个成员,包括apply、lapply、sapply、mapply和tapply等函数的使用方法及应用场景。通过具体示例展示了如何利用这些函数对数据进行高效处理。

R语言中有几个常用的函数,可以按组对数据进行处理,apply, lapply, sapply, tapply, mapply,等。这几个函数功能有些类似,下面介绍下这几个函数的用法。

Apply

这是对一个Matrix或者Array进行某个维度的运算。其格式是:

Apply(数据,维度Index,运算函数,函数的参数)

对于Matrix来说,其维度值为2,第二个参数维度Index中,1表示按行运算,2表示按列运算。下面举一个例子:

m<-matrix(1:6,2,3)

构建一个简单的2行3列的矩阵,内容为:

     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
如果我们要计算每一行的sum值,那么我们可以写为:
apply(m,1,sum)
[1]  9 12
如果要计算每一列的mean值,那么改为:
apply(m,2,mean)
[1] 1.5 3.5 5.5
假如某个值为NA,那么要忽略NA值,进行每一行的SUM怎么办呢?
m[2,2]<-NA
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2   NA    6
apply(m,1,sum)
[1]  9 NA
本身sum函数有一个参数na.rm,我们可以将这个参数带人到apply函数中,作为第4个参数:
apply(m,1,sum,na.rm=TRUE)
[1] 9 8
需要注意的是如果是Data Frame,那么系统会将其转为Matrix,如果所有Column不是数字类型或者类型不一致,导致转换失败,那么apply是运算不出任何一列的结果的。

Lapply

前面说到apply是对于matrix和array的,针对list,我们可以使用lapply函数。该函数接收list,返回的结果也是一个list。其调用如下:
Apply(数据,运算函数,函数的参数)
对于Data Frame来说,如果不同的列有不同的数据类型,不能转换成Matrix,但是却可以转换成List,然后使用lapply函数。
我们建立一个学生名字,年龄和成绩的Data Frame,然后统计平均年龄和平均成绩,由于name列不是数值类型,所以无法算平均值,所以我们可以对非数值的数据只取count数量。这里就需要用到自定义函数。
函数可以是匿名函数,也可以是之前定义好的函数,由于这里逻辑简单,我们可以用匿名函数解决。

s<-data.frame(name=c("Devin","Edward","Lulu"),age=c(30,33,29),score=c(95,99,90))

    name age score
1  Devin  30    95
2 Edward  33    99
3   Lulu  29    90


lapply(s,function(x){if(is.numeric(x)){mean(x)}else{length(x)}})

$name
[1] 3

$age
[1] 30.66667

$score
[1] 94.66667
我们可以看到返回了一个List的结果,里面包含3个项,每个项是函数执行的结果。lapply返回的结果和传入的List的结构相同,传入多少个Item,返回的也是多少个Item。

Sapply

Sapply函数和Lapply函数很类似,也是对List进行处理,只是在返回结果上,Sapply会根据结果的数据类型和结构,重新构建一个合理的数据类型返回。调用格式如下:
Apply(数据,运算函数,函数的参数,simplify = TRUE, USE.NAMES = TRUE)
对于其中的simplify参数,就是指明是否对返回的结果集重新组织,如果为FALSE,那么就相当于lapply了。USE.NAMES是对字符串数据处理时,是否使用字符串作为命名的。
还是上面的例子,只是把lapply换成sapply:
sapply(s,function(x){if(is.numeric(x)){mean(x)}else{length(x)}})
    name      age    score 
 3.00000 30.66667 94.66667 
我们可以看到结果集变成了一个数字向量,而不是List了。

Mapply

这是对多个数据(multivariate)进行sapply处理,只是调用是参数位置有所变化,先把函数放前面:
mapply(运算函数,函数的参数,第一个传入参数,第二个数据…,SIMPLIFY = TRUE,USE.NAMES = TRUE)
比如我们自定义一个函数m3,接受3个数值参数,然后将3个数字相乘返回结果:
m3<-function(a,b,c){a*b*c}
然后我们构建3个向量,他们具有相同的长度:

a<-1:5
b<-2:6
c<-5:1

现在我们要求a,b,c中的对应各位数进行m3函数的运算,也就是把a,b,c的第一个数做运算,然后把a,b,c的第二个数做运算,然后第三个数~~~这时候就用mapply很方便:

mapply(m3,a,b,c)
[1] 10 24 36 40 30

OK,就这么简单,实现了对应的各位元素的运算。

Tapply

前面介绍的几个apply函数都是对整体数据进行处理,而tapply是对向量中的数据进行分组处理。先看看tapply函数的调用格式:
tapply(向量数据,分组标识,运算函数,函数的参数,simplify = TRUE)
我们以一个学生数据的Data Frame为例来讲解tapply函数,先构建一个新的学生数据,包含name,age,score,class,gender:

s<-data.frame(name=c("Devin","Edward","Lulu","Jeneen"),age=c(30,33,29,32),score=c(95,99,90,88),class=c(1,2,1,2),gender=c("M","M","F","F"))

    name age score class gender
1  Devin  30    95     1      M
2 Edward  33    99     2      M
3   Lulu  29    90     1      F
4 Jeneen  32    88     2      F

如果我们要计算每个班的平均成绩,那么使用tapply的方法是:

tapply(s$score,s$class,mean)
   1    2 
92.5 93.5 
如果改为按gender算平均成绩,那么就是:
tapply(s$score,s$gender,mean)
 F  M 
89 97 
如果同时按class和gender来看呢?这里就需要把两个向量构建成list作为第二个参数传入:
tapply(s$score,list(s$class,s$gender),mean)
   F  M
1 90 95
2 88 99
 

转载于:https://www.cnblogs.com/studyzy/p/4355082.html

<think>好的,我现在需要处理用户关于Java中Map的函数式编程的询问。首先,我得回忆一下用户的历史对话。之前他们问过Python的爱心代码,现在转向Java的Map函数式编程,可能是在学习不同语言的特性,或者有项目需求。 用户可能想要了解如何利用Java 8引入的Stream API和函数式接口来更高效地操作Map。他们可能已经熟悉基本的Map操作,比如put、get,但想用更简洁、函数式的方式处理数据。需要确定用户的水平,但根据之前的提问,可能属于中级,正在探索高级特性。 首先,我需要整理Java中Map的函数式方法,比如forEach、computeIfAbsent、merge、replaceAll等。这些方法在Java 8之后引入,允许使用lambda表达式,提升代码的可读性和简洁性。要举一些具体的例子,比如遍历、转换、合并等操作。 同时,用户可能想知道如何结合Stream API来处理Map的entrySet、keySet或values。比如将Map转换为流,进行过滤、映射、收集等操作。这部分需要详细说明,尤其是entrySet().stream()的使用。 还要注意线程安全的问题,比如ConcurrentHashMap的函数式方法,如forEach、search、reduce等,适用于并发环境。这可能对有高并发需求的用户有帮助。 需要确保回答结构清晰,分点讲解,每个方法都有示例代码和说明。比如先介绍Map自身的函数式方法,再讲如何结合Stream使用,最后提到并发情况下的应用。 可能用户的实际需求是优化现有代码,减少循环和条件判断,使用函数式编程使代码更简洁高效。因此,示例代码要简单明了,突出函数式编程的优势,比如更少的代码行数,更高的可维护性。 还要注意避免常见的错误,比如在遍历时修改Map的结构,导致ConcurrentModificationException,可以建议使用ConcurrentHashMap或适当的同步措施。 最后,总结使用函数式编程的好处,如代码简洁、易于并行处理、提高可读性等,并给出应用场景的建议,比如数据处理、转换、过滤等。</think>在 Java 中,Map 的函数式编程主要通过 Java 8 引入的 Stream API 和函数式接口实现。以下是关键方法和应用场景的详细说明: --- ### 一、Map 自带的函数式方法 #### 1. `forEach(BiConsumer)` **遍历键值对**(替代传统循环) ```java Map<String, Integer> map = Map.of("A", 1, "B", 2); map.forEach((k, v) -> System.out.println(k + " → " + v) ); ``` #### 2. `computeIfAbsent(K, Function)` / `computeIfPresent` **条件性计算值** ```java Map<String, List<Integer>> cache = new HashMap<>(); String key = "data"; // 不存在时初始化列表 List<Integer> list = cache.computeIfAbsent(key, k -> new ArrayList<>()); list.add(42); ``` #### 3. `merge(K, V, BiFunction)` **合并冲突值** ```java Map<String, Integer> scores = new HashMap<>(); scores.merge("Alice", 90, (oldVal, newVal) -> oldVal + newVal); // 首次添加 90 scores.merge("Alice", 10, (oldVal, newVal) -> oldVal + newVal); // 累加为 100 ``` #### 4. `replaceAll(BiFunction)` **批量修改值** ```java Map<String, Integer> map = new HashMap<>(Map.of("a", 1, "b", 2)); map.replaceAll((k, v) -> v * 10); // {a=10, b=20} ``` --- ### 二、结合 Stream API 的高级操作 #### 1. 过滤 Entry ```java Map<String, Integer> filtered = map.entrySet().stream() .filter(entry -> entry.getValue() > 100) .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue )); ``` #### 2. 键值转换 ```java Map<String, String> transformed = map.entrySet().stream() .collect(Collectors.toMap( entry -> entry.getKey().toUpperCase(), entry -> String.valueOf(entry.getValue() * 2) )); ``` #### 3. 分组统计 ```java List<String> words = Arrays.asList("apple", "banana", "cherry"); Map<Integer, Long> lengthCount = words.stream() .collect(Collectors.groupingBy( String::length, Collectors.counting() )); // 结果:{5=1, 6=2} ``` --- ### 三、并发场景下的函数式操作 使用 `ConcurrentHashMap` 的原子方法: ```java ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>(); // 线程安全的累加操作 concurrentMap.compute("counter", (k, v) -> (v == null) ? 1 : v + 1 ); // 并行搜索 Integer result = concurrentMap.search(1, (k, v) -> v > 1000 ? k : null ); ``` --- ### 四、实用技巧 1. **链式操作**: ```java map.computeIfAbsent("key", k -> new ArrayList<>()) .add("newValue"); ``` 2. **默认值处理**: ```java Integer value = map.getOrDefault("unknown", 0); ``` 3. **不可变 Map**: ```java Map<String, Integer> immutable = Map.ofEntries( entry("A", 1), entry("B", 2) ); ``` --- ### 五、性能注意事项 1. 优先使用 `entrySet().stream()` 而非 `keySet().stream()` 或 `values().stream()` 2. 并行流 (`parallelStream()`) 适合大数据量处理 3. 频繁更新的场景建议使用 `ConcurrentHashMap` 通过结合这些函数式特性,可以显著提升 Map 数据处理的简洁性和效率,特别是在数据转换、过滤和统计场景中效果尤为明显。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值